﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые для проверки прав.

// Проверяет, что у пользователя есть роль в одном из профилей тех групп доступа, в которых он участвует,
// например: роль ПросмотрЖурналаРегистрации, роль ПечатьНепроведенныхДокументов.
//
// Если указан объект (или наборы значений доступа), тогда дополнительно проверяется,
// что группа доступа предоставляет право Чтение указанного объекта (или набор значений доступа разрешен).
//
// Параметры:
//  Роль           - Строка - имя роли.
//
//  СсылкаНаОбъект - ЛюбаяСсылка - на объект, для которого заполняются наборы значений доступа
//                   для проверки права Чтение.
//                 - ТаблицаЗначений - таблица произвольных наборов значений доступа с колонками:
//                     * НомерНабора     - Число  - номер, группирующий несколько строк в отдельный набор.
//                     * ВидДоступа      - Строка - имя вида доступа, указанного в переопределяемом модуле.
//                     * ЗначениеДоступа - ОпределяемыйТип.ЗначениеДоступа - тип значения доступа,
//                       указанного в переопределяемом модуле.
//                       Пустую подготовленную таблицу можно получить с помощью функции
//                       ТаблицаНаборыЗначенийДоступа общего модуля УправлениеДоступом
//                       (колонки Чтение, Изменение не заполнять).
//
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - если параметр не указан,
//                     тогда право проверяется для текущего пользователя.
//
// Возвращаемое значение:
//  Булево - если Истина, тогда у пользователя есть роль с учетом ограничений.
//
Функция ЕстьРоль(Знач Роль, Знач СсылкаНаОбъект = Неопределено, Знач Пользователь = Неопределено) Экспорт
	
	Пользователь = ?(ЗначениеЗаполнено(Пользователь), Пользователь, Пользователи.АвторизованныйПользователь());
	Если Пользователи.ЭтоПолноправныйПользователь(Пользователь) Тогда
		Возврат Истина;
	КонецЕсли;
	Роль = ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Роль." + Роль);
	
	УстановитьПривилегированныйРежим(Истина);
	
	Если СсылкаНаОбъект = Неопределено ИЛИ НЕ ОграничиватьДоступНаУровнеЗаписей() Тогда
		// Проверка, что роль назначается пользователю через группу доступа по профилю.
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("АвторизованныйПользователь", Пользователь);
		Запрос.УстановитьПараметр("Роль", Роль);
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
		|		ПО (СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь)
		|			И (СоставыГруппПользователей.ГруппаПользователей = ГруппыДоступаПользователи.Пользователь)
		|			И (СоставыГруппПользователей.Используется)
		|			И (НЕ ГруппыДоступаПользователи.Ссылка.ПометкаУдаления)
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК ПрофилиГруппДоступаРоли
		|		ПО ГруппыДоступаПользователи.Ссылка.Профиль = ПрофилиГруппДоступаРоли.Ссылка
		|			И (ПрофилиГруппДоступаРоли.Роль = &Роль)
		|			И (НЕ ПрофилиГруппДоступаРоли.Ссылка.ПометкаУдаления)";
		Возврат НЕ Запрос.Выполнить().Пустой();
	КонецЕсли;
		
	Если ТипЗнч(СсылкаНаОбъект) = Тип("ТаблицаЗначений") Тогда
		НаборыЗначенийДоступа = СсылкаНаОбъект.Скопировать();
	Иначе
		НаборыЗначенийДоступа = ТаблицаНаборыЗначенийДоступа();
		СсылкаНаОбъект.ПолучитьОбъект().ЗаполнитьНаборыЗначенийДоступа(НаборыЗначенийДоступа);
		// Выбор только наборов значений доступа, предназначенных для проверки права Чтение.
		СтрокиНаборовЧтения = НаборыЗначенийДоступа.НайтиСтроки(Новый Структура("Чтение", Истина));
		НомераНаборов = Новый Соответствие;
		Для каждого Строка Из СтрокиНаборовЧтения Цикл
			НомераНаборов.Вставить(Строка.НомерНабора, Истина);
		КонецЦикла;
		Индекс = НаборыЗначенийДоступа.Количество() - 1;
		Пока Индекс >= 0 Цикл
			Если НомераНаборов[НаборыЗначенийДоступа[Индекс].НомерНабора] = Неопределено Тогда
				НаборыЗначенийДоступа.Удалить(Индекс);
			КонецЕсли;
			Индекс = Индекс - 1;
		КонецЦикла;
		НаборыЗначенийДоступа.ЗаполнитьЗначения(Ложь, "Чтение, Изменение");
	КонецЕсли;
	
	// Уточнение наборов значений доступа.
	ИменаВидовДоступа = УправлениеДоступомСлужебный.СвойстваВидовДоступа().ПоИменам;
	
	Для каждого Строка Из НаборыЗначенийДоступа Цикл
		
		Если Строка.ВидДоступа = "" Тогда
			Продолжить;
		КонецЕсли;
		
		Если ВРег(Строка.ВидДоступа) = ВРег("ПравоЧтения")
		 ИЛИ ВРег(Строка.ВидДоступа) = ВРег("ПравоИзменения") Тогда
			
			Если ТипЗнч(Строка.ЗначениеДоступа) <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
				Если ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Строка.ЗначениеДоступа)) Тогда
					Строка.ЗначениеДоступа = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ТипЗнч(Строка.ЗначениеДоступа));
				Иначе
					Строка.ЗначениеДоступа = Неопределено;
				КонецЕсли;
			КонецЕсли;
			
			Если ВРег(Строка.ВидДоступа) = ВРег("ПравоИзменения") Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Ошибка в функции %1 модуля %2.
					           |В наборе значений доступа указан вид доступа %3
					           |таблицы с идентификатором ""%4"".
					           |В ограничении проверки роли (как дополнительного права)
					           |может быть зависимость только от права Чтения.'"),
					"ЕстьРоль",
					"УправлениеДоступом",
					"ПравоИзменения",
					Строка.ЗначениеДоступа,
					"Чтения");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		ИначеЕсли ИменаВидовДоступа.Получить(Строка.ВидДоступа) <> Неопределено
		      ИЛИ Строка.ВидДоступа = "НастройкиПрав" Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка в функции %1 модуля %2.
				           |Набор значений доступа содержит известный вид доступа ""%3"",
				           |который не требуется указывать.
				           |
				           |Указывать требуется только специальные виды доступа
				           |""%4"", ""%5"", если они используются.'"),
				"ЕстьРоль",
				"УправлениеДоступом",
				Строка.ВидДоступа,
				"ПравоЧтения",
				"ПравоИзменения");
			ВызватьИсключение ТекстОшибки;
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка в функции %1 модуля %2.
				           |Набор значений доступа содержит неизвестный вид доступа ""%1"".'"),
				"ЕстьРоль",
				"УправлениеДоступом",
				Строка.ВидДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Строка.ВидДоступа = "";
	КонецЦикла;
	
	// Добавление служебных полей в набор значений доступа.
	УправлениеДоступомСлужебный.ПодготовитьНаборыЗначенийДоступаКЗаписи(Неопределено, НаборыЗначенийДоступа, Истина);
	
	// Проверка, что роль назначается пользователю через группу доступа по профилю с разрешенными наборами значений
	// доступа.
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("АвторизованныйПользователь", Пользователь);
	Запрос.УстановитьПараметр("Роль", Роль);
	Запрос.УстановитьПараметр("НаборыЗначенийДоступа", НаборыЗначенийДоступа);
	Запрос.УстановитьПараметр("ТипыВладельцевНастроекПрав", ПараметрыСеанса.ТипыВладельцевНастроекПрав);
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НаборыЗначенийДоступа.НомерНабора,
	|	НаборыЗначенийДоступа.ЗначениеДоступа,
	|	НаборыЗначенийДоступа.ЗначениеБезГрупп,
	|	НаборыЗначенийДоступа.СтандартноеЗначение
	|ПОМЕСТИТЬ НаборыЗначенийДоступа
	|ИЗ
	|	&НаборыЗначенийДоступа КАК НаборыЗначенийДоступа
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	НаборыЗначенийДоступа.НомерНабора,
	|	НаборыЗначенийДоступа.ЗначениеДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ГруппыДоступаПользователи.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ ГруппыДоступа
	|ИЗ
	|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь)
	|			И (СоставыГруппПользователей.ГруппаПользователей = ГруппыДоступаПользователи.Пользователь)
	|			И (СоставыГруппПользователей.Используется)
	|			И (НЕ ГруппыДоступаПользователи.Ссылка.ПометкаУдаления)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК ПрофилиГруппДоступаРоли
	|		ПО ГруппыДоступаПользователи.Ссылка.Профиль = ПрофилиГруппДоступаРоли.Ссылка
	|			И (ПрофилиГруппДоступаРоли.Роль = &Роль)
	|			И (НЕ ПрофилиГруппДоступаРоли.Ссылка.ПометкаУдаления)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Наборы.НомерНабора
	|ПОМЕСТИТЬ НомераНаборов
	|ИЗ
	|	НаборыЗначенийДоступа КАК Наборы
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Наборы.НомерНабора
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	НЕ(ИСТИНА В
	|					(ВЫБРАТЬ ПЕРВЫЕ 1
	|						ИСТИНА
	|					ИЗ
	|						НомераНаборов КАК НомераНаборов
	|					ГДЕ
	|						ИСТИНА В
	|							(ВЫБРАТЬ ПЕРВЫЕ 1
	|								ИСТИНА
	|							ИЗ
	|								НаборыЗначенийДоступа КАК НаборыЗначений
	|							ГДЕ
	|								НаборыЗначений.НомерНабора = НомераНаборов.НомерНабора
	|								И НЕ ИСТИНА В
	|										(ВЫБРАТЬ ПЕРВЫЕ 1
	|											ИСТИНА
	|										ИЗ
	|											РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|										ГДЕ
	|											ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|											И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(НаборыЗначений.ЗначениеДоступа)
	|											И ЗначенияПоУмолчанию.БезНастройки = ИСТИНА)))
	|				И НЕ ИСТИНА В
	|						(ВЫБРАТЬ ПЕРВЫЕ 1
	|							ИСТИНА
	|						ИЗ
	|							НомераНаборов КАК НомераНаборов
	|						ГДЕ
	|							ИСТИНА В
	|								(ВЫБРАТЬ ПЕРВЫЕ 1
	|									ИСТИНА
	|								ИЗ
	|									НаборыЗначенийДоступа КАК НаборыЗначений
	|								ГДЕ
	|									НаборыЗначений.НомерНабора = НомераНаборов.НомерНабора
	|									И НЕ ИСТИНА В
	|											(ВЫБРАТЬ ПЕРВЫЕ 1
	|												ИСТИНА
	|											ИЗ
	|												РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|											ГДЕ
	|												ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|												И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(НаборыЗначений.ЗначениеДоступа)
	|												И ЗначенияПоУмолчанию.БезНастройки = ИСТИНА))
	|							И НЕ ЛОЖЬ В
	|									(ВЫБРАТЬ ПЕРВЫЕ 1
	|										ЛОЖЬ
	|									ИЗ
	|										НаборыЗначенийДоступа КАК НаборыЗначений
	|									ГДЕ
	|										НаборыЗначений.НомерНабора = НомераНаборов.НомерНабора
	|										И НЕ ВЫБОР
	|												КОГДА НаборыЗначений.ЗначениеБезГрупп
	|													ТОГДА ИСТИНА В
	|															(ВЫБРАТЬ ПЕРВЫЕ 1
	|																ИСТИНА
	|															ИЗ
	|																РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|																	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияГруппДоступа КАК Значения
	|																	ПО
	|																		Значения.ГруппаДоступа = ГруппыДоступа.Ссылка
	|																			И Значения.ЗначениеДоступа = НаборыЗначений.ЗначениеДоступа
	|															ГДЕ
	|																ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|																И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(НаборыЗначений.ЗначениеДоступа)
	|																И ЕСТЬNULL(Значения.ЗначениеРазрешено, ЗначенияПоУмолчанию.ВсеРазрешены))
	|												КОГДА НаборыЗначений.СтандартноеЗначение
	|													ТОГДА ВЫБОР
	|															КОГДА ИСТИНА В
	|																	(ВЫБРАТЬ ПЕРВЫЕ 1
	|																		ИСТИНА
	|																	ИЗ
	|																		РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначенийДоступа
	|																	ГДЕ
	|																		ГруппыЗначенийДоступа.ЗначениеДоступа = НаборыЗначений.ЗначениеДоступа
	|																		И ГруппыЗначенийДоступа.ГруппаЗначенийДоступа = &АвторизованныйПользователь)
	|																ТОГДА ИСТИНА
	|															ИНАЧЕ ИСТИНА В
	|																	(ВЫБРАТЬ ПЕРВЫЕ 1
	|																		ИСТИНА
	|																	ИЗ
	|																		РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|																			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначений
	|																			ПО
	|																				ГруппыЗначений.ЗначениеДоступа = НаборыЗначений.ЗначениеДоступа
	|																					И ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|																					И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(НаборыЗначений.ЗначениеДоступа)
	|																			ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияГруппДоступа КАК Значения
	|																			ПО
	|																				Значения.ГруппаДоступа = ГруппыДоступа.Ссылка
	|																					И Значения.ЗначениеДоступа = ГруппыЗначений.ГруппаЗначенийДоступа
	|																	ГДЕ
	|																		ЕСТЬNULL(Значения.ЗначениеРазрешено, ЗначенияПоУмолчанию.ВсеРазрешены))
	|														КОНЕЦ
	|												КОГДА НаборыЗначений.ЗначениеДоступа = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ДоступРазрешен)
	|													ТОГДА ИСТИНА
	|												КОГДА НаборыЗначений.ЗначениеДоступа = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ДоступЗапрещен)
	|													ТОГДА ЛОЖЬ
	|												КОГДА ТИПЗНАЧЕНИЯ(НаборыЗначений.ЗначениеДоступа) = ТИП(Справочник.ИдентификаторыОбъектовМетаданных)
	|													ТОГДА ИСТИНА В
	|															(ВЫБРАТЬ ПЕРВЫЕ 1
	|																ИСТИНА
	|															ИЗ
	|																РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступаПроверкаПраваНаОбъект
	|															ГДЕ
	|																ТаблицыГруппДоступаПроверкаПраваНаОбъект.ГруппаДоступа = ГруппыДоступа.Ссылка
	|																И ТаблицыГруппДоступаПроверкаПраваНаОбъект.Таблица = НаборыЗначений.ЗначениеДоступа)
	|												ИНАЧЕ ИСТИНА В
	|															(ВЫБРАТЬ ПЕРВЫЕ 1
	|																ИСТИНА
	|															ИЗ
	|																РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|																	ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|																	ПО
	|																		НаследованиеНастроек.Объект = НаборыЗначений.ЗначениеДоступа
	|																			И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|																			И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньРазрешенияЧтения
	|																	ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|																	ПО
	|																		СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь
	|																			И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|														И НЕ ЛОЖЬ В
	|																(ВЫБРАТЬ ПЕРВЫЕ 1
	|																	ЛОЖЬ
	|																ИЗ
	|																	РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|																		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|																		ПО
	|																			НаследованиеНастроек.Объект = НаборыЗначений.ЗначениеДоступа
	|																				И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|																				И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияЧтения
	|																		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|																		ПО
	|																			СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь
	|																				И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|											КОНЕЦ)))";
	
	Возврат НЕ Запрос.Выполнить().Пустой();
	
КонецФункции

// Проверяет, что для пользователя настроено разрешение права для объекта.
//  Например, для папки файлов могут быть настроены права "УправлениеПравами", "Чтение", "ИзменениеПапок",
// причем право "Чтение" является как правом для папки файлов, так и правом для файлов.
//
// Параметры:
//  Право          - Строка - имя права, как оно указано в процедуре ПриЗаполненииВозможныхПравДляНастройкиПравОбъектов
//                   общего модуля УправлениеДоступомПереопределяемый.
//
//  СсылкаНаОбъект - СправочникСсылка
//                 - ПланВидовХарактеристикСсылка - ссылка на одного из владельцев прав,
//                   указанных в процедуре ПриЗаполненииВозможныхПравДляНастройкиПравОбъектов
//                   общего модуля УправлениеДоступомПереопределяемый, например ссылка на папку файлов.
//
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - если параметр не указан,
//                     тогда право проверяется для текущего пользователя.
//
// Возвращаемое значение:
//  Булево - если Истина, то настроено разрешение права с учетом всех разрешающих и
//           запрещающих настроек в иерархии.
//
Функция ЕстьПраво(Право, СсылкаНаОбъект, Знач Пользователь = Неопределено) Экспорт
	
	УчитыватьПривилегированныйРежим = Истина;
	Если ЗначениеЗаполнено(Пользователь) Тогда
		УчитыватьПривилегированныйРежим = Ложь;
	Иначе
		Пользователь = Пользователи.АвторизованныйПользователь();
	КонецЕсли;
	Если Пользователи.ЭтоПолноправныйПользователь(Пользователь,, УчитыватьПривилегированныйРежим) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Не ОграничиватьДоступНаУровнеЗаписей()
	 Или УправлениеДоступомСлужебныйПовтИсп.ЭтоПользовательБезОграниченияДоступа(Пользователь) Тогда
		Возврат Истина;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ВозможныеПрава = УправлениеДоступомСлужебный.ВозможныеПраваДляНастройкиПравОбъектов();
	ОписаниеПрав = ВозможныеПрава.ПоТипам.Получить(ТипЗнч(СсылкаНаОбъект));
	
	Если ОписаниеПрав = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Нет описания возможных прав для таблицы ""%1""'"),
			СсылкаНаОбъект.Метаданные().ПолноеИмя());
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ОписаниеПрава = ОписаниеПрав.Получить(Право);
	
	Если ОписаниеПрава = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Нет описания права ""%1"" для таблицы ""%2""'"),
			Право, СсылкаНаОбъект.Метаданные().ПолноеИмя());
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если НЕ ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("СсылкаНаОбъект", СсылкаНаОбъект);
	Запрос.УстановитьПараметр("Пользователь", Пользователь);
	Запрос.УстановитьПараметр("Право", Право);
	
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|					ПО
	|						НаследованиеНастроек.Объект = &СсылкаНаОбъект
	|							И НастройкиПрав.Право = &Право
	|							И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньРазрешенияПрава
	|							И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|					ПО
	|						СоставыГруппПользователей.Пользователь = &Пользователь
	|							И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|	И НЕ ЛОЖЬ В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ЛОЖЬ
	|				ИЗ
	|					РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|						ПО
	|							НаследованиеНастроек.Объект = &СсылкаНаОбъект
	|								И НастройкиПрав.Право = &Право
	|								И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияПрава
	|								И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|						ПО
	|							СоставыГруппПользователей.Пользователь = &Пользователь
	|								И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)";
	
	Возврат НЕ Запрос.Выполнить().Пустой();
	
КонецФункции

// Проверяет, что на уровне записей (и на уровне прав) указанному пользователю
// разрешено чтение объекта из базы данных.
// Когда указан набор записей проверяются все записи в базе данных,
// которые соответствуют свойству Отбор.
//
// Важно: если подсистема работает в режиме стандартного ограничения и
// указан пользователь, но не текущий пользователь, то будет вызвано
// исключение (для проверки предусмотрена функция ПроизводительныйВариант).
//
// Параметры:
//  ОписаниеДанных - СправочникСсылка
//                 - ДокументСсылка
//                 - ПланВидовХарактеристикСсылка
//                 - ПланСчетовСсылка
//                 - ПланВидовРасчетаСсылка
//                 - БизнесПроцессСсылка
//                 - ЗадачаСсылка
//                 - ПланОбменаСсылка - ссылка на объект, который требуется проверить.
//                 - РегистрСведенийКлючЗаписи
//                 - РегистрНакопленияКлючЗаписи
//                 - РегистрБухгалтерииКлючЗаписи
//                 - РегистрРасчетаКлючЗаписи - ключ записи, которую требуется проверить.
//                 - СправочникОбъект
//                 - ДокументОбъект
//                 - ПланВидовХарактеристикОбъект
//                 - ПланСчетовОбъект
//                 - ПланВидовРасчетаОбъект
//                 - БизнесПроцессОбъект
//                 - ЗадачаОбъект
//                 - ПланОбменаОбъект - объект в памяти и базе данных, который требуется проверить.
//                 - РегистрСведенийНаборЗаписей
//                 - РегистрНакопленияНаборЗаписей
//                 - РегистрБухгалтерииНаборЗаписей
//                 - РегистрРасчетаНаборЗаписей - набор записей с настроенным свойством Отбор,
//                     по которому определяются записи в базе данных для проверки.
//
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - текущий пользователь.
//                   Когда параметр указан, привилегированный режим не учитывается.
//                   Для проверки прав не текущего пользователя требуются административные права.
//
// Возвращаемое значение:
//  Булево
//
Функция ЧтениеРазрешено(ОписаниеДанных, Пользователь = Неопределено) Экспорт
	
	Возврат УправлениеДоступомСлужебный.ДоступРазрешен(ОписаниеДанных, Ложь,,, Пользователь);
	
КонецФункции

// Проверяет, что на уровне записей (и на уровне прав) указанному пользователю
// разрешено изменение объекта в базе данных на объект в памяти.
// Для нового объекта проверяется только объект в памяти.
// Если указана ссылка или ключ записи, проверятся только объект в базе данных.
//
// Важно: если подсистема работает в режиме стандартного ограничения,
// а не в режиме универсального ограничения, тогда проверяется право
// Изменение на таблицу, а на уровне записей проверяется только право Чтение.
// Если указан пользователь, но не текущий пользователь, то будет вызвано
// исключение (для проверки предусмотрена функция ПроизводительныйВариант).
//
// Параметры:
//  ОписаниеДанных - СправочникСсылка
//                 - ДокументСсылка
//                 - ПланВидовХарактеристикСсылка
//                 - ПланСчетовСсылка
//                 - ПланВидовРасчетаСсылка
//                 - БизнесПроцессСсылка
//                 - ЗадачаСсылка
//                 - ПланОбменаСсылка - ссылка на объект в базе данных, который требуется проверить.
//                 - РегистрСведенийКлючЗаписи
//                 - РегистрНакопленияКлючЗаписи
//                 - РегистрБухгалтерииКлючЗаписи
//                 - РегистрРасчетаКлючЗаписи - ключ записи в базе данных, которую требуется проверить.
//                 - СправочникОбъект
//                 - ДокументОбъект
//                 - ПланВидовХарактеристикОбъект
//                 - ПланСчетовОбъект
//                 - ПланВидовРасчетаОбъект
//                 - БизнесПроцессОбъект
//                 - ЗадачаОбъект
//                 - ПланОбменаОбъект - объект в памяти и базе данных, который требуется проверить.
//                 - РегистрСведенийНаборЗаписей
//                 - РегистрНакопленияНаборЗаписей
//                 - РегистрБухгалтерииНаборЗаписей
//                 - РегистрРасчетаНаборЗаписей - набор записей в памяти и базе данных,
//                                                который требуется проверить.
//
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - текущий пользователь.
//                   Когда параметр указан, привилегированный режим не учитывается.
//                   Для проверки прав не текущего пользователя требуются административные права.
//
// Возвращаемое значение:
//  Булево
//
Функция ИзменениеРазрешено(ОписаниеДанных, Пользователь = Неопределено) Экспорт
	
	Возврат УправлениеДоступомСлужебный.ДоступРазрешен(ОписаниеДанных, Истина,,, Пользователь);
	
КонецФункции

// Делает тоже, что функция ЧтениеРазрешено, но если не разрешено, вызывается исключение.
// 
// Параметры:
//  ОписаниеДанных - см. ЧтениеРазрешено.ОписаниеДанных
//
Процедура ПроверитьЧтениеРазрешено(ОписаниеДанных) Экспорт
	
	УправлениеДоступомСлужебный.ДоступРазрешен(ОписаниеДанных, Ложь, Истина);
	
КонецПроцедуры

// Делает тоже, что функция ИзменениеРазрешено, но если не разрешено, вызывается исключение.
// 
// Параметры:
//  ОписаниеДанных - см. ИзменениеРазрешено.ОписаниеДанных
//
Процедура ПроверитьИзменениеРазрешено(ОписаниеДанных) Экспорт
	
	УправлениеДоступомСлужебный.ДоступРазрешен(ОписаниеДанных, Истина, Истина);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры для включения и исключения пользователя в профиле групп доступа.

// Назначение пользователю профиля групп доступа путем включения
// в персональную группу доступа (только для упрощенной настройки прав).
//
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - требуемый пользователь.
//  Профиль      - СправочникСсылка.ПрофилиГруппДоступа - профиль, для которого нужно найти или создать персональную
//                   группу доступа и включить в нее пользователя.
//               - УникальныйИдентификатор - уникальный идентификатор поставляемого профиля,
//                   с помощью которого нужно найти профиль групп доступа.
//               - Строка - имя поставляемого профиля, 
//                   с помощью которого нужно найти профиль групп доступа.
//
Процедура ВключитьПрофильПользователю(Пользователь, Профиль) Экспорт
	ВключитьОтключитьПрофильПользователя(Пользователь, Профиль, Истина);
КонецПроцедуры

// Отмена назначения пользователю профиля групп доступа путем исключения
// из персональной группы доступа (только для упрощенной настройки прав).
//
// Параметры:
//  Пользователь - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - требуемый пользователь.
//  Профиль      - СправочникСсылка.ПрофилиГруппДоступа - профиль, для которого нужно найти или создать персональную
//                    группу доступа и включить в нее пользователя.
//               - УникальныйИдентификатор - уникальный идентификатор поставляемого профиля,
//                    с помощью которого нужно найти профиль групп доступа.
//               - Строка - имя поставляемого профиля,
//                    с помощью которого нужно найти профиль групп доступа.
//               - Неопределено - выключить пользователю все профили.
//
Процедура ВыключитьПрофильПользователю(Пользователь, Профиль = Неопределено) Экспорт
	ВключитьОтключитьПрофильПользователя(Пользователь, Профиль, Ложь);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для получения общих настроек подсистемы.

// Проверяет, используется ли ограничение доступа на уровне записей.
//
// Возвращаемое значение:
//  Булево - если Истина, тогда доступ ограничивается на уровне записей.
//
Функция ОграничиватьДоступНаУровнеЗаписей() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	УстановитьОтключениеБезопасногоРежима(Истина);
	
	Результат = УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписей();
	
	УстановитьОтключениеБезопасногоРежима(Ложь);
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат Результат;
	
КонецФункции

// Возвращает вариант работы ограничений доступа на уровне записей.
//
// Требуется при расширенном использовании функций
// ЧтениеРазрешено и ИзменениеРазрешено в производительном варианте.
//
// Возвращаемое значение:
//  Булево - если Ложь, стандартный вариант, иначе производительный.
//
Функция ПроизводительныйВариант() Экспорт
	
	Возврат УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально(Ложь, Истина);
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые для настройки интерфейса управляемых форм.

// Обработчик события формы ПриЧтенииНаСервере, который встраивается в формы элементов справочников,
// документов, записей регистров и др., чтобы заблокировать форму, если изменение запрещено.
//
// Параметры:
//  Форма               - ФормаКлиентскогоПриложения - форма элемента объекта или записи регистра.
//
//  ТекущийОбъект       - СправочникОбъект
//                      - ДокументОбъект
//                      - ПланВидовХарактеристикОбъект
//                      - ПланСчетовОбъект
//                      - ПланВидовРасчетаОбъект
//                      - БизнесПроцессОбъект
//                      - ЗадачаОбъект
//                      - ПланОбменаОбъект - проверяемый объект.
//                      - РегистрСведенийМенеджерЗаписи - менеджер проверяемой записи.
//                      - РегистрСведенийНаборЗаписей
//                      - РегистрНакопленияНаборЗаписей
//                      - РегистрБухгалтерииНаборЗаписей
//                      - РегистрРасчетаНаборЗаписей - набор проверяемых записей.
//
Процедура ПриЧтенииНаСервере(Форма, ТекущийОбъект) Экспорт
	
	Если УправлениеДоступомСлужебный.ДоступРазрешен(ТекущийОбъект, Истина, Ложь, Истина) Тогда
		Возврат;
	КонецЕсли;
	
	Форма.ТолькоПросмотр = Истина;
	
КонецПроцедуры

// Обработчик события формы ПослеЗаписиНаСервере, который встраивается в формы
// элементов справочников, документов, записей регистров и др., чтобы ускорить
// запуск обновления доступа зависимых объектов, когда обновление запланировано.
//
// Параметры:
//  Форма           - ФормаКлиентскогоПриложения - форма элемента объекта или записи регистра.
//
//  ТекущийОбъект   - СправочникОбъект
//                  - ДокументОбъект
//                  - ПланВидовХарактеристикОбъект
//                  - ПланСчетовОбъект
//                  - ПланВидовРасчетаОбъект
//                  - БизнесПроцессОбъект
//                  - ЗадачаОбъект
//                  - ПланОбменаОбъект - проверяемый объект.
//                  - РегистрСведенийМенеджерЗаписи - менеджер проверяемой записи.
//
//  ПараметрыЗаписи - Структура - стандартный параметр, передаваемый в обработчик события.
//
Процедура ПослеЗаписиНаСервере(Форма, ТекущийОбъект, ПараметрыЗаписи) Экспорт
	
	УправлениеДоступомСлужебный.ЗапуститьОбновлениеДоступа();
	
КонецПроцедуры

// АПК:142-выкл - №640.5 Необязательных параметров более 3 для обратной совместимости.

// Настраивает форму значения доступа, которое использует группы значений доступа
// для выбора разрешенных значений в группах доступа пользователей.
//
// Поддерживается только для случая, когда у значения доступа выбирается одна группа значений доступа,
// а не несколько.
//
// Для элемента формы ГруппаДоступа, связанного с реквизитом ГруппаДоступа, устанавливает
// список групп значений доступа в параметр выбора, которые дают доступ на изменение значения доступа.
//
// При создании нового значения доступа, если количество групп значений доступа, которые дают доступ
// на изменение значения доступа, равно нулю, будет вызвано исключение.
//
// Если в базе данных уже записана группа значений доступа, которая не дает доступ на изменение значения доступа,
// или количество групп значений доступа, которые дают доступ на изменение значения доступа, равно нулю,
// тогда свойство формы ТолькоПросмотр устанавливается в Истина.
//
// Если ограничение на уровне записей не используется или ограничение по виду доступа не используется,
// тогда элемент формы скрывается.
//
// Параметры:
//  Форма - ФормаКлиентскогоПриложения - форма значения доступа,
//            использующего группы для выбора разрешенных значений.
//
//  ДополнительныеПараметры - см. ПараметрыПриСозданииФормыЗначенияДоступа
//
//  УдалитьЭлементы       - Неопределено - устарел, следует использовать ДополнительныеПараметры.
//  УдалитьТипЗначения    - Неопределено - устарел, следует использовать ДополнительныеПараметры.
//  УдалитьСозданиеНового - Неопределено - устарел, следует использовать ДополнительныеПараметры.
//
Процедура ПриСозданииФормыЗначенияДоступа(Форма, ДополнительныеПараметры = Неопределено,
			УдалитьЭлементы = Неопределено, УдалитьТипЗначения = Неопределено, УдалитьСозданиеНового = Неопределено) Экспорт
	
	Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
		Реквизит       = ДополнительныеПараметры.Реквизит;
		Элементы       = ДополнительныеПараметры.Элементы;
		ТипЗначения    = ДополнительныеПараметры.ТипЗначения;
		СозданиеНового = ДополнительныеПараметры.СозданиеНового;
	Иначе
		Реквизит       = ДополнительныеПараметры;
		Элементы       = УдалитьЭлементы;
		ТипЗначения    = УдалитьТипЗначения;
		СозданиеНового = УдалитьСозданиеНового;
	КонецЕсли;
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1
		           |общего модуля %2.'"),
		"ПриСозданииФормыЗначенияДоступа",
		"УправлениеДоступом");
	
	Если ТипЗнч(СозданиеНового) <> Тип("Булево") Тогда
		Попытка
			ОбъектФормы = Форма.Объект; // ОпределяемыйТип.ЗначениеДоступа - реально ДанныеФормыСтруктура объекта.
			СозданиеНового = НЕ ЗначениеЗаполнено(ОбъектФормы.Ссылка);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Параметр %1 не указан, а автоматическое заполнение
				           |из реквизита формы ""%2"" недоступно по причине:
				           |%3'"),
				"СозданиеНового",
				"Объект.Ссылка",
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	КонецЕсли;
	
	Если ТипЗнч(ТипЗначения) <> Тип("Тип") Тогда
		Попытка
			ОбъектФормы = Форма.Объект; // ОпределяемыйТип.ЗначениеДоступа - реально ДанныеФормыСтруктура объекта.
			ТипЗначенияДоступа = ТипЗнч(ОбъектФормы.Ссылка);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Параметр %1 не указан, а автоматическое заполнение
				           |из реквизита формы ""%2"" недоступно по причине:
				           |%3'"),
				"ТипЗначения",
				"Объект.Ссылка",
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	Иначе
		ТипЗначенияДоступа = ТипЗначения;
	КонецЕсли;
	
	Если Элементы = Неопределено Тогда
		ЭлементыФормы = Новый Массив;
		ЭлементыФормы.Добавить("ГруппаДоступа");
		
	ИначеЕсли ТипЗнч(Элементы) <> Тип("Массив") Тогда
		ЭлементыФормы = Новый Массив;
		ЭлементыФормы.Добавить(Элементы);
	КонецЕсли;
	
	СвойстваГрупп = СвойстваГруппЗначенияДоступа(ТипЗначенияДоступа, ЗаголовокОшибки);
	
	Если Реквизит = Неопределено Тогда
		Попытка
			ГруппаЗначенийДоступа = Форма.Объект.ГруппаДоступа;
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Параметр Реквизит не указан, а автоматическое заполнение
				           |из реквизита формы ""%2"" недоступно по причине:
				           |%3'"),
				"Реквизит",
				"Объект.ГруппаДоступа",
				ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	Иначе
		ПозицияТочки = СтрНайти(Реквизит, ".");
		Если ПозицияТочки = 0 Тогда
			Попытка
				ГруппаЗначенийДоступа = Форма[Реквизит];
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось получить значение реквизита формы ""%1"",
					           |указанного параметре %2 по причине:
					           |%3'"),
					Реквизит,
					"Реквизит",
					ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
				ВызватьИсключение ТекстОшибки;
			КонецПопытки;
		Иначе
			Попытка
				ГруппаЗначенийДоступа = Форма[Лев(Реквизит, ПозицияТочки - 1)][Сред(Реквизит, ПозицияТочки + 1)];
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось получить значение реквизита формы ""%1"",
					           |указанного параметре %2 по причине:
					           |%3'"),
					Реквизит,
					"Реквизит",
					ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
				ВызватьИсключение ТекстОшибки;
			КонецПопытки;
		КонецЕсли;
	КонецЕсли;
	
	Если ТипЗнч(ГруппаЗначенийДоступа) <> СвойстваГрупп.Тип Тогда
		ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для значений доступа типа ""%1""
			           |используются вид доступа ""%2"" с типом значений ""%3"",
			           |заданным в переопределяемом модуле.
			           |Но этот тип не совпадает с типом ""%4"" в форме значения
			           |доступа у реквизита %5.'"),
			Строка(ТипЗначенияДоступа),
			Строка(СвойстваГрупп.ВидДоступа),
			Строка(СвойстваГрупп.Тип),
			Строка(ТипЗнч(ГруппаЗначенийДоступа)),
			"ГруппаДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не УправлениеДоступомСлужебный.ВидДоступаИспользуется(СвойстваГрупп.ВидДоступа) Тогда
		Для Каждого Элемент Из ЭлементыФормы Цикл
			Форма.Элементы[Элемент].Видимость = Ложь;
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	Если Пользователи.ЭтоПолноправныйПользователь( , , Ложь) Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ПравоДоступа("Изменение", Метаданные.НайтиПоТипу(ТипЗначенияДоступа)) Тогда
		Форма.ТолькоПросмотр = Истина;
		Возврат;
	КонецЕсли;
	
	ГруппыЗначенийДляИзменения =
		ГруппыЗначенийДоступаРазрешающиеИзменениеЗначенийДоступа(ТипЗначенияДоступа);
	
	Если ГруппыЗначенийДляИзменения.Количество() = 0
	   И СозданиеНового Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для добавления требуются разрешенные ""%1"".'"),
			Метаданные.НайтиПоТипу(СвойстваГрупп.Тип).Представление());
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ГруппыЗначенийДляИзменения.Количество() = 0
	 ИЛИ НЕ СозданиеНового
	   И ГруппыЗначенийДляИзменения.Найти(ГруппаЗначенийДоступа) = Неопределено Тогда
		
		Форма.ТолькоПросмотр = Истина;
		Возврат;
	КонецЕсли;
	
	Если СозданиеНового
	   И НЕ ЗначениеЗаполнено(ГруппаЗначенийДоступа)
	   И ГруппыЗначенийДляИзменения.Количество() = 1 Тогда
		
		Если Реквизит = Неопределено Тогда
			Форма.Объект.ГруппаДоступа = ГруппыЗначенийДляИзменения[0];
		Иначе
			ПозицияТочки = СтрНайти(Реквизит, ".");
			Если ПозицияТочки = 0 Тогда
				Форма[Реквизит] = ГруппыЗначенийДляИзменения[0];
			Иначе
				Форма[Лев(Реквизит, ПозицияТочки - 1)][Сред(Реквизит, ПозицияТочки + 1)] = ГруппыЗначенийДляИзменения[0];
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	НовыйПараметрВыбора = Новый ПараметрВыбора(
		"Отбор.Ссылка", Новый ФиксированныйМассив(ГруппыЗначенийДляИзменения));
	
	ПараметрыВыбора = Новый Массив;
	ПараметрыВыбора.Добавить(НовыйПараметрВыбора);
	
	Для каждого Элемент Из ЭлементыФормы Цикл
		Форма.Элементы[Элемент].ПараметрыВыбора = Новый ФиксированныйМассив(ПараметрыВыбора);
	КонецЦикла;
	
КонецПроцедуры

// АПК:142-вкл.

// Описание дополнительных параметров, используемых в процедуре ПриСозданииФормыЗначенияДоступа.
// 
// Возвращаемое значение:
//  Структура:
//    * Реквизит       - Неопределено - означает имя реквизита формы "Объект.ГруппаДоступа".
//                     - Строка - имя реквизита формы, содержащего группу доступа.
//
//    * Элементы       - Неопределено - означает имя элемента формы "ГруппаДоступа".
//                     - Строка - имя элемента формы.
//                     - Массив - имена элементов формы.
//
//    * ТипЗначения    - Неопределено - означает: получить тип из реквизита формы "Объект.Ссылка".
//                     - Тип - тип ссылки значения доступа.
//
//    * СозданиеНового - Неопределено - означает: получить значение "НЕ ЗначениеЗаполнено(Форма.Объект.Ссылка)"
//                       для определения того, создается новое значение доступа или нет.
//                     - Булево - используется указанное значение.
//
Функция ПараметрыПриСозданииФормыЗначенияДоступа() Экспорт
	
	Возврат Новый Структура("Реквизит, Элементы, ТипЗначения, СозданиеНового");
	
КонецФункции

// Возвращает массив групп значений доступа, разрешающих изменять значения доступа.
//
// Поддерживается только для случая, когда выбирается одна группа значений доступа, а не несколько.
//
// Параметры:
//  ТипЗначенийДоступа - Тип - тип ссылки значений доступа.
//  ВозвращатьВсе      - Булево - если Истина, то в случае, когда нет ограничений
//                       (доступны все), будет возвращен массив всех вместо Неопределено.
//
// Возвращаемое значение:
//  Неопределено - все группы значений доступа разрешают изменять значения доступа.
//  Массив       - массив найденных групп значений доступа.
//
Функция ГруппыЗначенийДоступаРазрешающиеИзменениеЗначенийДоступа(ТипЗначенийДоступа, ВозвращатьВсе = Ложь) Экспорт
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1
		           |общего модуля %2.'"),
		"ГруппыЗначенийДоступаРазрешающиеИзменениеЗначенийДоступа",
		"УправлениеДоступом");
	
	СвойстваГрупп = СвойстваГруппЗначенияДоступа(ТипЗначенийДоступа, ЗаголовокОшибки);
	
	Если Не ПравоДоступа("Чтение", Метаданные.НайтиПоТипу(СвойстваГрупп.Тип)) Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	Если Не УправлениеДоступомСлужебный.ВидДоступаИспользуется(СвойстваГрупп.ВидДоступа)
	 Или Пользователи.ЭтоПолноправныйПользователь( , , Ложь) Тогда
		
		Если ВозвращатьВсе Тогда
			Запрос = Новый Запрос;
			Запрос.Текст =
			"ВЫБРАТЬ РАЗРЕШЕННЫЕ
			|	ГруппыЗначенийДоступа.Ссылка КАК Ссылка
			|ИЗ
			|	&ТаблицаГруппЗначенийДоступа КАК ГруппыЗначенийДоступа";
			Запрос.Текст = СтрЗаменить(
				Запрос.Текст, "&ТаблицаГруппЗначенийДоступа", СвойстваГрупп.Таблица);
			
			Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
		КонецЕсли;
		
		Возврат Неопределено;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТекущийПользователь", Пользователи.АвторизованныйПользователь());
	Запрос.УстановитьПараметр("ТипЗначенийДоступа",  СвойстваГрупп.ПустаяСсылкаТипаЗначений);
	
	Запрос.УстановитьПараметр("ИдентификаторЗначенийДоступа",
		ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ТипЗначенийДоступа));
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка
	|ПОМЕСТИТЬ ГруппыДоступаПользователя
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
	|			ГДЕ
	|				ТаблицыГруппДоступа.Таблица = &ИдентификаторЗначенийДоступа
	|				И ТаблицыГруппДоступа.ГруппаДоступа = ГруппыДоступа.Ссылка
	|				И ТаблицыГруппДоступа.ПравоИзменение = ИСТИНА)
	|	И ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|					ПО
	|						СоставыГруппПользователей.Используется
	|							И СоставыГруппПользователей.Пользователь = &ТекущийПользователь
	|							И ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей
	|							И ГруппыДоступаПользователи.Ссылка = ГруппыДоступа.Ссылка)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ГруппыЗначенийДоступа.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ ГруппыЗначений
	|ИЗ
	|	&ТаблицаГруппЗначенийДоступа КАК ГруппыЗначенийДоступа
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				ГруппыДоступаПользователя КАК ГруппыДоступаПользователя
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|					ПО
	|						ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступаПользователя.Ссылка
	|							И ЗначенияПоУмолчанию.ТипЗначенийДоступа = &ТипЗначенийДоступа
	|					ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияГруппДоступа КАК Значения
	|					ПО
	|						Значения.ГруппаДоступа = ГруппыДоступаПользователя.Ссылка
	|							И Значения.ЗначениеДоступа = ГруппыЗначенийДоступа.Ссылка
	|			ГДЕ
	|				ЕСТЬNULL(Значения.ЗначениеРазрешено, ЗначенияПоУмолчанию.ВсеРазрешены))";
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТаблицаГруппЗначенийДоступа", СвойстваГрупп.Таблица);
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
	
	УстановитьПривилегированныйРежим(Истина);
	Запрос.Выполнить();
	УстановитьПривилегированныйРежим(Ложь);
	
	Запрос.Текст =
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	ГруппыЗначенийДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	&ТаблицаГруппЗначенийДоступа КАК ГруппыЗначенийДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыЗначений КАК ГруппыЗначений
	|		ПО ГруппыЗначенийДоступа.Ссылка = ГруппыЗначений.Ссылка";
	
	Запрос.Текст = СтрЗаменить(
		Запрос.Текст, "&ТаблицаГруппЗначенийДоступа", СвойстваГрупп.Таблица);
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Устанавливает в условие ГДЕ динамического списка постоянные отборы
// на основе разрешенных значений доступа указанных типов в рамках всех групп доступа.
// Это позволяет в отдельных случаях ускорить открытие динамического списка.
// Если количество разрешенных значений в общей сложности получилось более 100, отбор не устанавливается.
//
// Для работы процедуры у динамического списка должна быть установлена основная таблица,
// установлен произвольный запрос, а также должно поддерживаться преобразование вида:
//   СхемаЗапроса = Новый СхемаЗапроса;
//   СхемаЗапроса.УстановитьТекстЗапроса(Список.ТекстЗапроса);
//   Список.ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
// Если это условие выполнить не удается, тогда следует добавлять отборы самостоятельно
// с использованием функции РазрешенныеЗначенияДляДинамическогоСписка, как в этой процедуре.
//
// Параметры:
//  Список          - ДинамическийСписок - динамический список, в который нужно установить отборы.
//  ОписаниеОтборов - Соответствие из КлючИЗначение:
//    * Ключ     - Строка - имя поля основной таблицы динамического списка, для которого
//                          нужно установить условие <Поле> В (&РазрешенныеЗначения).
//    * Значение - Тип    - тип значений доступа, которые нужно включить в состав
//                          параметра "&РазрешенныеЗначения".
//               - Массив - массив указанных выше типов.
//
Процедура НастроитьОтборыДинамическогоСписка(Список, ОписаниеОтборов) Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписей()
	 Или УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально(Ложь, Истина)
	 Или Пользователи.ЭтоПолноправныйПользователь(,, Ложь) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Список) <> Тип("ДинамическийСписок") Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка вызова процедуры %1 общего модуля %2.
			           |Значение параметра %3 ""%4"" не является динамическим списком.'"),
			"НастроитьОтборыДинамическогоСписка",
			"УправлениеДоступом",
			"Список",
			Строка(Список));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(Список.ОсновнаяТаблица) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка вызова процедуры %1 общего модуля %2.
			           |У переданного динамического списка не указана основная таблица.'"),
			"НастроитьОтборыДинамическогоСписка",
			"УправлениеДоступом");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не Список.ПроизвольныйЗапрос Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка вызова процедуры %1 общего модуля %2.
			           |У переданного динамического списка не установлен флажок %3.'"),
			"НастроитьОтборыДинамическогоСписка",
			"УправлениеДоступом",
			"ПроизвольныйЗапрос");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	СхемаЗапроса = Новый СхемаЗапроса;
	СхемаЗапроса.УстановитьТекстЗапроса(Список.ТекстЗапроса);
	Параметры = Новый Соответствие;
	
	Для Каждого ОписаниеОтбора Из ОписаниеОтборов Цикл
		ИмяПоля = ОписаниеОтбора.Ключ;
		Значения = УправлениеДоступомСлужебный.РазрешенныеЗначенияДляДинамическогоСписка(
			Список.ОсновнаяТаблица, ОписаниеОтбора.Значение);
		Если Значения = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		Источники = СхемаЗапроса.ПакетЗапросов[0].Операторы[0].Источники;
		Псевдоним = "";
		Для Каждого Источник Из Источники Цикл
			Если Источник.Источник.ИмяТаблицы = Список.ОсновнаяТаблица Тогда
				Псевдоним = Источник.Источник.Псевдоним;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не ЗначениеЗаполнено(Псевдоним) Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка вызова процедуры %1 общего модуля %2.
				           |У переданного динамического списка не удалось найти псевдоним основной таблицы
				           |""%1"".'"),
				"НастроитьОтборыДинамическогоСписка",
				"УправлениеДоступом",
				Список.ОсновнаяТаблица);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Отбор = СхемаЗапроса.ПакетЗапросов[0].Операторы[0].Отбор;
		ИмяПараметра = "РазрешенныеЗначенияПоля" + ИмяПоля;
		Параметры.Вставить(ИмяПараметра, Значения);
		
		Условие = Псевдоним + "." + ИмяПоля + " В (&" + ИмяПараметра + ")";
		Отбор.Добавить(Условие);
	КонецЦикла;
	
	Список.ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
	
	Для Каждого КлючИЗначение Из Параметры Цикл
		ОбновитьЗначениеПараметраКомпоновкиДанных(Список, КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
КонецПроцедуры

// Возвращает массив разрешенных значений указанных типов в рамках всех групп доступа.
// Используется в процедуре НастроитьОтборыДинамическогоСписка для ускорения открытия динамических списков.
// 
// Параметры:
//  Таблица      - Строка - полное имя объекта метаданных, например "Документ.РасходнаяНакладная".
//  ТипЗначений  - Тип    - тип значений доступа, разрешенные значения которых нужно вернуть.
//               - Массив - массив указанных выше типов.
//  Пользователь - Неопределено - вернуть разрешенные значения для авторизованного пользователя.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - вернуть
//                   разрешенные значения для указанного пользователя.
//  ВернутьВсе   - Булево - если установить в Истина, тогда будут возвращены все значения - даже тогда,
//                   когда их более 100.
//
// Возвращаемое значение:
//  Неопределено - либо все значения разрешены для типов, указанных в параметре ТипЗначений,
//                 либо (когда ВернутьВсе = Ложь) количество разрешенных значений превышает 100.
//  Массив       - ссылки разрешенных значений указанных типов.
//
Функция РазрешенныеЗначенияДляДинамическогоСписка(Таблица, ТипЗначений, Пользователь = Неопределено, ВернутьВсе = Ложь) Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписей()
	 Или Пользователи.ЭтоПолноправныйПользователь(Пользователь, , Ложь) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат УправлениеДоступомСлужебный.РазрешенныеЗначенияДляДинамическогоСписка(Таблица, ТипЗначений, , Пользователь, ВернутьВсе);
	
КонецФункции

// Возвращает права доступа к объектам метаданных ссылочного типа по указанным идентификаторам.
//
// Параметры:
//  Идентификаторы - Массив - значения СправочникСсылка.ИдентификаторыОбъектовМетаданных,
//                            объектов ссылочного типа, для которых нужно вернуть права.
//
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//    * Ключ     - СправочникСсылка.ИдентификаторыОбъектовМетаданных - идентификатор типа;
//    * Значение - Структура:
//        ** Ключ     - Строка - имя права доступа ("Чтение", "Изменение", "Добавление");
//        ** Значение - Булево - если Истина, тогда право есть, иначе нет.
//
Функция ПраваПоИдентификаторам(Идентификаторы = Неопределено) Экспорт
	
	ОбъектыМетаданныхИдентификаторов =
		ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(Идентификаторы);
	
	ПраваПоИдентификаторам = Новый Соответствие;
	Для Каждого ОбъектМетаданныхИдентификатора Из ОбъектыМетаданныхИдентификаторов Цикл
		ОбъектМетаданных = ОбъектМетаданныхИдентификатора.Значение;
		Права = Новый Структура;
		Права.Вставить("Чтение",     ПравоДоступа("Чтение",     ОбъектМетаданных));
		Права.Вставить("Изменение",  ПравоДоступа("Изменение",  ОбъектМетаданных));
		Права.Вставить("Добавление", ПравоДоступа("Добавление", ОбъектМетаданных));
		ПраваПоИдентификаторам.Вставить(ОбъектМетаданныхИдентификатора.Ключ, Права);
	КонецЦикла;
	
	Возврат ПраваПоИдентификаторам;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с наборами значений доступа.

// Проверяет, предусмотрена ли для объекта метаданных процедура заполнения наборов значений доступа.
// 
// Параметры:
//  Ссылка - ЛюбаяСсылка - ссылка на любой объект.
//
// Возвращаемое значение:
//  Булево - если Истина, наборы значений доступа можно заполнить.
//
Функция ВозможноЗаполнитьНаборыЗначенийДоступа(Ссылка) Экспорт
	
	ТипОбъекта = Тип(ОбщегоНазначения.ВидОбъектаПоСсылке(Ссылка) + "Объект." + Ссылка.Метаданные().Имя);
	
	НаборыЗаполняются = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьНаборыЗначенийДоступа
		|ЗаписатьЗависимыеНаборыЗначенийДоступа").Получить(ТипОбъекта) <> Неопределено;
	
	Возврат НаборыЗаполняются;
	
КонецФункции

// Возвращает пустую таблицу, которая заполняется для передачи в функцию ЕстьРоль и
// в процедуры ЗаполнитьНаборыЗначенийДоступа(Таблица), определенные прикладным разработчиком.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * НомерНабора     - Число  - необязательно, если набор один.
//    * ВидДоступа      - Строка - необязательно, кроме специальных: ПравоЧтения, ПравоИзменения.
//    * ЗначениеДоступа - ОпределяемыйТип.ЗначениеДоступа - тип значения доступа, указанный для вида доступа
//                        в процедуре ПриЗаполненииВидовДоступа общего модуля УправлениеДоступомПереопределяемый.
//    * Чтение          - Булево - необязательно, если набор для всех прав, устанавливается для одной строки набора.
//    * Изменение       - Булево - необязательно, если набор для всех прав, устанавливается для одной строки набора.
//
Функция ТаблицаНаборыЗначенийДоступа() Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("НомерНабора",     Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(4, 0, ДопустимыйЗнак.Неотрицательный)));
	Таблица.Колонки.Добавить("ВидДоступа",      Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
	Таблица.Колонки.Добавить("ЗначениеДоступа", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	Таблица.Колонки.Добавить("Чтение",          Новый ОписаниеТипов("Булево"));
	Таблица.Колонки.Добавить("Изменение",       Новый ОписаниеТипов("Булево"));
	// Служебное поле - его нельзя заполнять и менять (заполняется автоматически).
	Таблица.Колонки.Добавить("Уточнение",       Новый ОписаниеТипов("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	
	Возврат Таблица;
	
КонецФункции

// Заполняет наборы значений доступа для переданного значения Объект, вызывая
// процедуру ЗаполнитьНаборыЗначенийДоступа, определенную в модуле этого объекта,
// и возвращает их в параметре Таблица.
//
// Объекты должны быть включены в подписку на событие ЗаписатьНаборыЗначенийДоступа
// или ЗаписатьЗависимыеНаборыЗначенийДоступа.
//
// В модулях объектов должна быть размещена процедура обработчика, в которую передаются параметры:
//  Таблица - ТаблицаЗначений - возвращаемая функцией ТаблицаНаборыЗначенийДоступа.
//
// Далее пример процедуры обработчика для копирования в модули объектов.
//
//// См. УправлениеДоступом.ЗаполнитьНаборыЗначенийДоступа.
//Процедура ЗаполнитьНаборыЗначенийДоступа(Таблица) Экспорт
//	
//	// Логика ограничения:
//	// Чтения:    Организация.
//	// Изменения: Организация И Ответственный.
//	
//	// Чтение: набор № 1.
//	Строка = Таблица.Добавить();
//	Строка.НомерНабора     = 1;
//	Строка.Чтение          = Истина;
//	Строка.ЗначениеДоступа = Организация;
//	
//	// Изменение: набор № 2.
//	Строка = Таблица.Добавить();
//	Строка.НомерНабора     = 2;
//	Строка.Изменение       = Истина;
//	Строка.ЗначениеДоступа = Организация;
//	
//	Строка = Таблица.Добавить();
//	Строка.НомерНабора     = 2;
//	Строка.ЗначениеДоступа = Ответственный;
//	
//КонецПроцедуры
//
// Параметры:
//  Объект  - ЛюбаяСсылка
//          - ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект - ссылка или объект,
//            (элемент справочника, документ, бизнес-процесс, задача, план видов характеристик и т.п.),
//            для которого требуется заполнить наборы значений доступа.
//
//  Таблица - см. ТаблицаНаборыЗначенийДоступа
//          - Неопределено - возвращает в этом параметре подготовленные наборы значений доступа. 
//            Если передано Неопределено, то будет создана и заполнена новая таблица наборов значений доступа.
//
//  СсылкаНаПодчиненныйОбъект - ЛюбаяСсылка - используется, когда нужно заполнить наборы значений
//            доступа объекта-владельца для указанного подчиненного объекта.
//            Подробнее смотри УправлениеДоступомПереопределяемый.ПриЗаполненииЗависимостейПравДоступа.
//
Процедура ЗаполнитьНаборыЗначенийДоступа(Знач Объект, Таблица, Знач СсылкаНаПодчиненныйОбъект = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	// Если передана ссылка, тогда получить объект.
	// Объект не изменяется, а используется для вызова метода ЗаполнитьНаборыЗначенийДоступа().
	Объект = ?(Объект = Объект.Ссылка, Объект.ПолучитьОбъект(), Объект);
	СсылкаНаОбъект = Объект.Ссылка;
	ТипЗначенияОбъект = ТипЗнч(Объект);
	
	НаборыЗаполняются = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьНаборыЗначенийДоступа
		|ЗаписатьЗависимыеНаборыЗначенийДоступа").Получить(ТипЗначенияОбъект) <> Неопределено;
	
	Если НЕ НаборыЗаполняются Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверные параметры.
			           |Тип объекта ""%1""
			           |отсутствует в подписках на события %2, %3.'"),
			ТипЗначенияОбъект,
			"ЗаписатьНаборыЗначенийДоступа",
			"ЗаписатьЗависимыеНаборыЗначенийДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Таблица = ?(ТипЗнч(Таблица) = Тип("ТаблицаЗначений"), Таблица, ТаблицаНаборыЗначенийДоступа());
	Попытка
		Объект.ЗаполнитьНаборыЗначенийДоступа(Таблица);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1 ""%2""
			           |не сформировал набор значений доступа по причине:
			           |%3'"),
			ТипЗнч(СсылкаНаОбъект),
			Объект,
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		ВызватьИсключение ТекстОшибки;
	КонецПопытки;
	
	Если Таблица.Количество() = 0 Тогда
		// Если это условие отключить, тогда зациклится регламентное задание
		// заполнения данных для ограничения доступа.
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1 ""%2""
			           |сформировал пустой набор значений доступа.'"),
			ТипЗнч(СсылкаНаОбъект),
			Объект);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	УточнитьНаборыЗначенийДоступа(СсылкаНаОбъект, Таблица);
	
	Если СсылкаНаПодчиненныйОбъект = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	// Добавление наборов проверки прав Чтения, Изменения "ведущего" объекта-владельца,
	// при формировании зависимых наборов значений в процедурах,
	// подготовленных прикладным разработчиком.
	//
	// Действие не требуется при заполнении конечного набора (даже включающего зависимые наборы),
	// т.к. в стандартных шаблонах проверка прав встроена в логику работы вида доступа "Объект".
	
	// Добавление пустого набора, чтобы установить все флажки прав и упорядочить строки наборов.
	ДобавитьНаборыЗначенийДоступа(Таблица, ТаблицаНаборыЗначенийДоступа());
	
	// Подготовка наборов объекта по отдельным правам.
	НаборыЧтения     = ТаблицаНаборыЗначенийДоступа();
	НаборыИзменения  = ТаблицаНаборыЗначенийДоступа();
	Для каждого Строка Из Таблица Цикл
		Если Строка.Чтение Тогда
			НоваяСтрока = НаборыЧтения.Добавить();
			НоваяСтрока.НомерНабора     = Строка.НомерНабора + 1;
			НоваяСтрока.ВидДоступа      = Строка.ВидДоступа;
			НоваяСтрока.ЗначениеДоступа = Строка.ЗначениеДоступа;
			НоваяСтрока.Уточнение       = Строка.Уточнение;
		КонецЕсли;
		Если Строка.Изменение Тогда
			НоваяСтрока = НаборыИзменения.Добавить();
			НоваяСтрока.НомерНабора     = (Строка.НомерНабора + 1)*2;
			НоваяСтрока.ВидДоступа      = Строка.ВидДоступа;
			НоваяСтрока.ЗначениеДоступа = Строка.ЗначениеДоступа;
			НоваяСтрока.Уточнение       = Строка.Уточнение;
		КонецЕсли;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.ЗависимостиПравДоступа КАК ЗависимостиПравДоступа
	|ГДЕ
	|	ЗависимостиПравДоступа.ПодчиненнаяТаблица = &ПодчиненнаяТаблица
	|	И ЗависимостиПравДоступа.ТипВедущейТаблицы = &ТипВедущейТаблицы";
	
	Запрос.УстановитьПараметр("ПодчиненнаяТаблица", ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		СсылкаНаПодчиненныйОбъект.Метаданные().ПолноеИмя()));
	
	МассивТипов = Новый Массив;
	МассивТипов.Добавить(ТипЗнч(СсылкаНаОбъект));
	ОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
	Запрос.УстановитьПараметр("ТипВедущейТаблицы", ОписаниеТипов.ПривестиЗначение(Неопределено));
	
	ЗависимостиПрав = Запрос.Выполнить().Выгрузить();
	Таблица.Очистить();
	
	Идентификатор = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ТипЗнч(СсылкаНаОбъект));
	
	Если ЗависимостиПрав.Количество() = 0 Тогда
		
		// Добавление наборов по стандартному правилу.
		
		// Проверка права Чтения "ведущего" объекта-владельца наборов
		// при проверке права Чтения "подчиненного" объекта.
		Строка = Таблица.Добавить();
		Строка.НомерНабора     = 1;
		Строка.ВидДоступа      = "ПравоЧтения";
		Строка.ЗначениеДоступа = Идентификатор;
		Строка.Чтение          = Истина;
		
		// Проверка права Изменения "ведущего" объекта-владельца наборов
		// при проверке прав Добавления, Изменения, Удаления "подчиненного" объекта.
		Строка = Таблица.Добавить();
		Строка.НомерНабора     = 2;
		Строка.ВидДоступа      = "ПравоИзменения";
		Строка.ЗначениеДоступа = Идентификатор;
		Строка.Изменение       = Истина;
		
		// Пометка прав, требующих проверки наборов ограничения права чтения "ведущего" объекта-владельца.
		НаборыЧтения.ЗаполнитьЗначения(Истина, "Чтение");
		// Пометка прав, требующих проверки наборов ограничения права изменения "ведущего" объекта-владельца.
		НаборыИзменения.ЗаполнитьЗначения(Истина, "Изменение");
		
		ДобавитьНаборыЗначенийДоступа(НаборыЧтения, НаборыИзменения);
		ДобавитьНаборыЗначенийДоступа(Таблица, НаборыЧтения, Истина);
	Иначе
		// Добавление наборов по нестандартному правилу: вместо изменения проверять чтение.
		
		// Проверка права Чтения "ведущего" объекта-владельца наборов
		// при проверке права Чтения "подчиненного" объекта.
		Строка = Таблица.Добавить();
		Строка.НомерНабора     = 1;
		Строка.ВидДоступа      = "ПравоЧтения";
		Строка.ЗначениеДоступа = Идентификатор;
		Строка.Чтение          = Истина;
		Строка.Изменение       = Истина;
		
		// Пометка прав, требующих проверки наборов ограничения права чтения "ведущего" объекта-владельца.
		НаборыЧтения.ЗаполнитьЗначения(Истина, "Чтение");
		НаборыЧтения.ЗаполнитьЗначения(Истина, "Изменение");
		ДобавитьНаборыЗначенийДоступа(Таблица, НаборыЧтения, Истина);
	КонецЕсли;
	
КонецПроцедуры

// Позволяет добавить к одной таблице наборов значений доступа другую таблицу
// наборов значений доступа либо логическим сложением, либо логическим умножением.
//
// Результат помещается в параметр Приемник.
//
// Параметры:
//  Приемник - ТаблицаЗначений - с колонками как у таблицы, возвращаемой функцией ТаблицаНаборыЗначенийДоступа.
//  Источник - ТаблицаЗначений - с колонками как у таблицы, возвращаемой функцией ТаблицаНаборыЗначенийДоступа.
//
//  Умножение - Булево - определяет способ логического объединения наборов приемника и источника.
//  Упростить - Булево - определяет, требуется ли упрощение наборов после добавления.
//
Процедура ДобавитьНаборыЗначенийДоступа(Приемник, Знач Источник, Знач Умножение = Ложь, Знач Упростить = Ложь) Экспорт
	
	Если Источник.Количество() = 0 И Приемник.Количество() = 0 Тогда
		Возврат;
		
	ИначеЕсли Умножение И ( Источник.Количество() = 0 ИЛИ  Приемник.Количество() = 0 ) Тогда
		Приемник.Очистить();
		Источник.Очистить();
		Возврат;
	КонецЕсли;
	
	Если Приемник.Количество() = 0 Тогда
		Значение = Приемник;
		Приемник = Источник;
		Источник = Значение;
	КонецЕсли;
	
	Если Упростить Тогда
		
		// Определение копий наборов и копий строк в наборах в пределах прав
		// в процессе добавления или умножения.
		//
		// Копии возникают из-за правил раскрытия скобок в логических выражениях:
		//  Для наборов в пределах права и наборов разных прав:
		//     X  И  X = X,
		//     X ИЛИ X = X, где X - набор строк-аргументов.
		//  Только для наборов в пределах права:
		//     (а И б И в) ИЛИ (а И б) = (а И б), где а,б,в - строки-аргументы наборов.
		// Исходя из этих правил одинаковые строки в наборе и одинаковые наборы можно удалить.
		
		Если Умножение Тогда
			УмножитьНаборыИУпростить(Приемник, Источник);
		Иначе // Добавление
			ДобавитьНаборыИУпростить(Приемник, Источник);
		КонецЕсли;
	Иначе
		
		Если Умножение Тогда
			УмножитьНаборы(Приемник, Источник);
		Иначе // Добавление
			ДобавитьНаборы(Приемник, Источник);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Выполняет обновление наборов значений доступа объекта, если они изменились.
// Наборы обновляются в табличной части (если используется) и
// в регистре сведений НаборыЗначенийДоступа.
//
// Параметры:
//  СсылкаИлиОбъект - ЛюбаяСсылка
//                  - ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект - ссылка или объект,
//                    для которого заполняются наборы значений доступа.
//  
//  ОбновлениеИБ    - Булево - если Истина, то необходимо выполнять запись данных, 
//                             не выполняя лишних, избыточных действий с данными.
//                             См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//
Процедура ОбновитьНаборыЗначенийДоступа(СсылкаИлиОбъект, ОбновлениеИБ = Ложь) Экспорт
	
	УправлениеДоступомСлужебный.ОбновитьНаборыЗначенийДоступа(СсылкаИлиОбъект,, ОбновлениеИБ);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые в переопределяемом модуле.

// Возвращает структуру для удобства описания поставляемых профилей.
//
// Возвращаемое значение:
//  Структура:
//   * Имя           - Строка - может использоваться в программном интерфейсе,
//                        например, в процедуре ВключитьПрофильПользователю.
//   * Родитель      - Строка - имя папки профилей, в которую входит профиль.
//   * Идентификатор - Строка - строка уникального идентификатора поставляемого
//                       профиля, которая используется для поиска в базе данных.
//                       Чтобы получить идентификатор, нужно создать профиль в режиме 1С:Предприятие и
//                       получить уникальный идентификатор ссылки. Не следует указывать идентификаторы,
//                       полученные произвольным способом, т.к. это может нарушить уникальность ссылок.
//   * Наименование  - Строка - наименование поставляемого профиля.
//   * Описание      - Строка - описание поставляемого профиля.
//   * Роли          - Массив из Строка - имена ролей поставляемого профиля.
//   * Назначение    - Массив из Тип - типы ссылок пользователя и объектов авторизации внешних
//                       пользователей. Если пустой, значит, назначение для пользователей.
//                       Должны быть в пределах состава определяемого типа Пользователь.
//   * ВидыДоступа   - СписокЗначений:
//                     ** Значение      - Строка - имя вида доступа, указанного в переопределяемом
//                          модуле УправлениеДоступомПереопределяемый в процедуре ПриЗаполненииВидовДоступа.
//                     ** Представление - Строка - допустимы строки "ВначалеВсеЗапрещены" (или пустая строка),
//                          "ВначалеВсеРазрешены", "Предустановленный".
//
//   * ЗначенияДоступа - СписокЗначений:
//                     ** Значение      - Строка - имя вида доступа, указанного в параметре ВидыДоступа.
//                     ** Представление - Строка - имя предопределенного элемента, например
//                          "Справочник.ГруппыПользователей.ВсеПользователи".
//
// Пример:
// 
//	// Профиль "Пользователь".
//	ОписаниеПрофиля = УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа();
//	ОписаниеПрофиля.Имя           = "Пользователь";
//	ОписаниеПрофиля.Идентификатор = "09e56dbf-90a0-11de-862c-001d600d9ad2";
//	ОписаниеПрофиля.Наименование  = НСтр("ru = 'Пользователь'", ОбщегоНазначения.КодОсновногоЯзыка());
//	// Переопределение назначения.
//	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(ОписаниеПрофиля.Назначение,
//		Метаданные.ОпределяемыеТипы.ВнешнийПользователь.Тип.Типы());
//	ОписаниеПрофиля.Описание =
//		НСтр("ru = 'Общие разрешенные действия для большинства пользователей.
//		           |Как правило, это права на просмотр данных информационной системы.'");
//	// Использование 1С:Предприятия.
//	ОписаниеПрофиля.Роли.Добавить("ЗапускТонкогоКлиента");
//	ОписаниеПрофиля.Роли.Добавить("ВыводНаПринтерФайлБуферОбмена");
//	ОписаниеПрофиля.Роли.Добавить("СохранениеДанныхПользователя");
//	// ...
//	// Использование программы.
//	ОписаниеПрофиля.Роли.Добавить("БазовыеПраваБСП");
//	ОписаниеПрофиля.Роли.Добавить("ПросмотрОписанияИзмененийПрограммы");
//	ОписаниеПрофиля.Роли.Добавить("ИзменениеТекущегоПользователя");
//	// ...
//	// Использование НСИ.
//	ОписаниеПрофиля.Роли.Добавить("ЧтениеБазовойНСИ");
//	ОписаниеПрофиля.Роли.Добавить("ЧтениеОбщейБазовойНСИ");
//	// ...
//	// Типовые возможности.
//	ОписаниеПрофиля.Роли.Добавить("ДобавлениеИзменениеЛичныхВариантовОтчетов");
//	ОписаниеПрофиля.Роли.Добавить("ПросмотрСвязанныеДокументы");
//	// ...
//	// Основные возможности профиля.
//	ОписаниеПрофиля.Роли.Добавить("ДобавлениеИзменениеЗаметок");
//	ОписаниеПрофиля.Роли.Добавить("ДобавлениеИзменениеНапоминаний");
//	ОписаниеПрофиля.Роли.Добавить("ДобавлениеИзменениеЗаданий");
//	ОписаниеПрофиля.Роли.Добавить("ИзменениеВыполнениеЗадач");
//	// ...
//	// Виды ограничения доступа профиля.
//	ОписаниеПрофиля.ВидыДоступа.Добавить("Организации");
//	ОписаниеПрофиля.ВидыДоступа.Добавить("Пользователи", "Предустановленный");
//	ОписаниеПрофиля.ВидыДоступа.Добавить("ХозяйственныеОперации", "Предустановленный");
//	ОписаниеПрофиля.ЗначенияДоступа.Добавить("ХозяйственныеОперации",
//		"Перечисление.ХозяйственныеОперации.ВыдачаДенежныхСредствПодотчетнику");
//	// ...
//	ОписанияПрофилей.Добавить(ОписаниеПрофиля);
//
Функция НовоеОписаниеПрофиляГруппДоступа() Экспорт
	
	НовоеОписание = Новый Структура;
	НовоеОписание.Вставить("Имя",             "");
	НовоеОписание.Вставить("Родитель",        "");
	НовоеОписание.Вставить("Идентификатор",   "");
	НовоеОписание.Вставить("Наименование",    "");
	НовоеОписание.Вставить("Описание",        "");
	НовоеОписание.Вставить("Роли",            Новый Массив);
	НовоеОписание.Вставить("Назначение",      Новый Массив);
	НовоеОписание.Вставить("ВидыДоступа",     Новый СписокЗначений);
	НовоеОписание.Вставить("ЗначенияДоступа", Новый СписокЗначений);
	
	Возврат НовоеОписание;
	
КонецФункции

// Возвращает структуру для удобства описания поставляемых папок профилей (групп элементов).
//
// Возвращаемое значение:
//  Структура:
//   * Имя           - Строка - используется в поле Родитель для профиля и папки профилей.
//   * Родитель      - Строка - имя другой папки профилей, в которую входит эта папка.
//   * Идентификатор - Строка - строка уникального идентификатора поставляемой папки
//                       профиля, которая используется для поиска в базе данных.
//                       Чтобы получить идентификатор, нужно создать папку профилей в режиме 1С:Предприятие и
//                       получить уникальный идентификатор ссылки. Не следует указывать идентификаторы,
//                       полученные произвольным способом, т.к. это может нарушить уникальность ссылок.
//   * Наименование  - Строка - наименование поставляемой папки профилей.
//
// Пример:
//	// Папка профилей "Дополнительные профили".
//	ОписаниеПапки = УправлениеДоступом.НовоеОписаниеПапкиПрофилейГруппДоступа();
//	ОписаниеПапки.Имя           = "ДополнительныеПрофили";
//	ОписаниеПапки.Идентификатор = "";
//	ОписаниеПапки.Наименование  = НСтр("ru = 'Дополнительные профили'", ОбщегоНазначения.КодОсновногоЯзыка());
//	// ...
//	ОписанияПрофилей.Добавить(ОписаниеПрофиля);
//
Функция НовоеОписаниеПапкиПрофилейГруппДоступа() Экспорт
	
	НовоеОписание = Новый Структура;
	НовоеОписание.Вставить("Имя",           "");
	НовоеОписание.Вставить("Родитель",      "");
	НовоеОписание.Вставить("Идентификатор", "");
	НовоеОписание.Вставить("Наименование",  "");
	
	Возврат НовоеОписание;
	
КонецФункции

// Добавляет дополнительные типы в процедуре ПриЗаполненииВидовДоступа
// общего модуля УправлениеДоступомПереопределяемый.
//
// Параметры:
//  ВидДоступа             - СтрокаТаблицыЗначений - добавленная в параметр ВидыДоступа.
//  ТипЗначений            - Тип - дополнительный тип значений доступа.
//  ТипГруппЗначений       - Тип - дополнительный тип групп значений доступа, может совпадать
//                           с типом групп значений, указанным ранее для этого же вида доступа.
//  НесколькоГруппЗначений - Булево - Истина, если у дополнительного типа значений доступа
//                           можно указать несколько групп значений (есть табличная часть ГруппыДоступа).
// 
Процедура ДобавитьДополнительныеТипыВидаДоступа(ВидДоступа, ТипЗначений,
		ТипГруппЗначений = Неопределено, НесколькоГруппЗначений = Ложь) Экспорт
	
	ДополнительныеТипы = ВидДоступа.ДополнительныеТипы; // См. УправлениеДоступомСлужебный.НоваяТаблицаДополнительныеТипыВидаДоступа
	
	Если ДополнительныеТипы.Колонки.Количество() = 0 Тогда
		ДополнительныеТипы = УправлениеДоступомСлужебный.НоваяТаблицаДополнительныеТипыВидаДоступа();
		ВидДоступа.ДополнительныеТипы = ДополнительныеТипы;
	КонецЕсли;
	
	ДополнительныеТипы = ВидДоступа.ДополнительныеТипы;
	
	НоваяСтрока = ДополнительныеТипы.Добавить();
	НоваяСтрока.ТипЗначений            = ТипЗначений;
	НоваяСтрока.ТипГруппЗначений       = ТипГруппЗначений;
	НоваяСтрока.НесколькоГруппЗначений = НесколькоГруппЗначений;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые при обновлении информационной базы.

// Заменяет роли в профилях, кроме поставляемых профилей, которые обновляются автоматически.
// Предназначена для вызова из оперативного обработчика обновления.
//
// Параметры:
//  ЗаменяемыеРоли - Соответствие из КлючИЗначение:
//    * Ключ     - Строка - имя заменяемой роли, например "ЧтениеБазовойНСИ". Если роль была удалена,
//                          то к имени нужно добавить приставку "? ", например "? ЧтениеБазовойНСИ".
//
//    * Значение - Массив - имена ролей для замены указанной (пустой массив, чтобы удалить указанную роль,
//                          можно указывать заменяемую роль, например, при разделении на несколько).
//
Процедура ЗаменитьРолиВПрофилях(ЗаменяемыеРоли) Экспорт
	
	ЗаменяемыеСсылкиРолей = Новый Соответствие;
	МассивЗаменяемыхРолей = Новый Массив;
	
	Для Каждого КлючИЗначение Из ЗаменяемыеРоли Цикл
		Если СтрНачинаетсяС(КлючИЗначение.Ключ, "? ") Тогда
			СсылкиРоли = Справочники.ИдентификаторыОбъектовМетаданных.ИдентификаторУдаленногоОбъектаМетаданных(
				"Роль." + СокрЛП(Сред(КлючИЗначение.Ключ, 3)));
		Иначе
			СсылкиРоли = Новый Массив;
			СсылкиРоли.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Роль." + КлючИЗначение.Ключ));
		КонецЕсли;
		Для Каждого СсылкаРоли Из СсылкиРоли Цикл
			МассивЗаменяемыхРолей.Добавить(СсылкаРоли);
			НовыеРоли = Новый Массив;
			ЗаменяемыеСсылкиРолей.Вставить(СсылкаРоли, НовыеРоли);
			Для Каждого НоваяРоль Из КлючИЗначение.Значение Цикл
				НовыеРоли.Добавить(ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Роль." + НоваяРоль));
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
	// Найти профили, использующие заменяемые роли.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("МассивЗаменяемыхРолей", МассивЗаменяемыхРолей);
	Запрос.УстановитьПараметр("ПустойИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	РолиПрофилей.Ссылка КАК Профиль,
	|	РолиПрофилей.Роль КАК Роль
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК Профили
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|		ПО (РолиПрофилей.Ссылка = Профили.Ссылка)
	|			И (РолиПрофилей.Роль В (&МассивЗаменяемыхРолей))
	|			И (Профили.ИдентификаторПоставляемыхДанных = &ПустойИдентификатор
	|				ИЛИ Профили.ПоставляемыйПрофильИзменен)
	|ИТОГИ ПО
	|	Профиль";
	
	ДеревоПрофилей = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	
	Для Каждого СтрокаПрофиля Из ДеревоПрофилей.Строки Цикл
		ЭлементБлокировки.УстановитьЗначение("Ссылка", СтрокаПрофиля.Профиль);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ПрофильОбъект = СтрокаПрофиля.Профиль.ПолучитьОбъект();
			РолиПрофиля = ПрофильОбъект.Роли;
		
			Для Каждого СтрокаРоли Из СтрокаПрофиля.Строки Цикл
				
				// Удаление заменяемой роли из профиля.
				Отбор = Новый Структура("Роль", СтрокаРоли.Роль);
				НайденныеСтроки = РолиПрофиля.НайтиСтроки(Отбор);
				Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл
					РолиПрофиля.Удалить(НайденнаяСтрока);
				КонецЦикла;
				
				// Добавление новых ролей в профиль вместо заменяемой роли.
				ДобавляемыеРоли = ЗаменяемыеСсылкиРолей.Получить(СтрокаРоли.Роль);
				
				Для Каждого ДобавляемаяРоль Из ДобавляемыеРоли Цикл
					Отбор = Новый Структура;
					Отбор.Вставить("Роль", ДобавляемаяРоль);
					Если РолиПрофиля.НайтиСтроки(Отбор).Количество() = 0 Тогда
						НоваяСтрока = РолиПрофиля.Добавить();
						НоваяСтрока.Роль = ДобавляемаяРоль;
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
			
			ОбновлениеИнформационнойБазы.ЗаписатьДанные(ПрофильОбъект);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
	Справочники.ПрофилиГруппДоступа.ОбновитьВспомогательныеДанныеПрофилей(
		ДеревоПрофилей.Строки.ВыгрузитьКолонку("Профиль"));
	
КонецПроцедуры

// Возвращает ссылку на поставляемый профиль или папку профилей по идентификатору.
//
// Параметры:
//  Идентификатор - Строка - имя или уникальный идентификатор поставляемого профиля или папки профилей,
//                  как указано в процедуре ПриЗаполненииПоставляемыхПрофилейГруппДоступа
//                  общего модуля УправлениеДоступомПереопределяемый.
//
// Возвращаемое значение:
//  СправочникСсылка.ПрофилиГруппДоступа - если поставляемый профиль или папка профилей найдена в справочнике.
//  Неопределено - если поставляемый профиль или папка профилей не существует в справочнике.
//
Функция ПоставляемыйПрофильПоИдентификатору(Идентификатор) Экспорт
	
	Возврат Справочники.ПрофилиГруппДоступа.ПоставляемыйПрофильПоИдентификатору(Идентификатор);
	
КонецФункции

// Возвращает ссылку на стандартный поставляемый профиль Администратор.
//
// Возвращаемое значение:
//  СправочникСсылка.ПрофилиГруппДоступа
//
Функция ПрофильАдминистратор() Экспорт
	
	Возврат Справочники.ПрофилиГруппДоступа.ПрофильАдминистратор();
	
КонецФункции

// Возвращает ссылку на стандартную поставляемую группу доступа Администраторы.
//
// Возвращаемое значение:
//  СправочникСсылка.ГруппыДоступа
//
Функция ГруппаДоступаАдминистраторы() Экспорт
	
	Возврат Справочники.ГруппыДоступа.ГруппаДоступаАдминистраторы();
	
КонецФункции

// Возвращает пустую таблицу для заполнения и
// передачи в процедуру ЗаменитьПраваВНастройкахПравОбъектов.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * ТипВладельцев - ОпределяемыйТип.ВладелецНастроекПрав - пустая ссылка типа владельца прав,
//                      например пустая ссылка справочника ПапкиФайлов.
//    * СтароеИмя     - Строка - старое имя права.
//    * НовоеИмя      - Строка - новое имя права.
//
Функция ТаблицаЗаменыПравВНастройкахПравОбъектов() Экспорт
	
	Измерения = Метаданные.РегистрыСведений.НастройкиПравОбъектов.Измерения;
	
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("ТипВладельцев", Измерения.Объект.Тип);
	Таблица.Колонки.Добавить("СтароеИмя",     Измерения.Право.Тип);
	Таблица.Колонки.Добавить("НовоеИмя",      Измерения.Право.Тип);
	
	Возврат Таблица;
	
КонецФункции

// Заменяет права, используемые в настройках прав объектов.
// После выполнения замены будет выполнено обновление вспомогательных данных
// регистра сведений НастройкиПравОбъектов, поэтому следует вызвать
// процедуру однократно, чтобы не снижать производительность.
// 
// Параметры:
//  ТаблицаПереименований - ТаблицаЗначений:
//    * ТипВладельцев - ОпределяемыйТип.ВладелецНастроекПрав - пустая ссылка типа владельца прав,
//                      например пустая ссылка справочника ПапкиФайлов.
//    * СтароеИмя     - Строка - старое имя права, относящееся к указанному типу владельцев.
//    * НовоеИмя      - Строка - новое  имя права, относящееся к указанному типу владельцев.
//                      Если указана пустая строка, настройка старого права будет удалена.
//                      Если старому имени поставлены в соответствие два новых имени,
//                      тогда одна старая настройка будет размножена в две новых.
//  
Процедура ЗаменитьПраваВНастройкахПравОбъектов(ТаблицаПереименований) Экспорт
	
	// АПК:96-выкл - №434 Использование ОБЪЕДИНИТЬ допустимо, так как
	// строки не должны повторятся и это разовая операция в обработчике обновления.
	Запрос = Новый Запрос;
	Запрос.Параметры.Вставить("ТаблицаПереименований", ТаблицаПереименований);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ТаблицаПереименований.ТипВладельцев,
	|	ТаблицаПереименований.СтароеИмя,
	|	ТаблицаПереименований.НовоеИмя
	|ПОМЕСТИТЬ ТаблицаПереименований
	|ИЗ
	|	&ТаблицаПереименований КАК ТаблицаПереименований
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПрав.Объект,
	|	НастройкиПрав.Пользователь,
	|	НастройкиПрав.Право,
	|	МАКСИМУМ(НастройкиПрав.ПравоЗапрещено) КАК ПравоЗапрещено,
	|	МАКСИМУМ(НастройкиПрав.НаследованиеРазрешено) КАК НаследованиеРазрешено,
	|	МАКСИМУМ(НастройкиПрав.ПорядокНастройки) КАК ПорядокНастройки
	|ПОМЕСТИТЬ СтарыеНастройкиПрав
	|ИЗ
	|	РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|
	|СГРУППИРОВАТЬ ПО
	|	НастройкиПрав.Объект,
	|	НастройкиПрав.Пользователь,
	|	НастройкиПрав.Право
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	СтарыеНастройкиПрав.Объект,
	|	СтарыеНастройкиПрав.Пользователь,
	|	ТаблицаПереименований.СтароеИмя,
	|	ТаблицаПереименований.НовоеИмя,
	|	СтарыеНастройкиПрав.ПравоЗапрещено,
	|	СтарыеНастройкиПрав.НаследованиеРазрешено,
	|	СтарыеНастройкиПрав.ПорядокНастройки
	|ПОМЕСТИТЬ НастройкиПрав
	|ИЗ
	|	СтарыеНастройкиПрав КАК СтарыеНастройкиПрав
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаПереименований КАК ТаблицаПереименований
	|		ПО (ТИПЗНАЧЕНИЯ(СтарыеНастройкиПрав.Объект) = ТИПЗНАЧЕНИЯ(ТаблицаПереименований.ТипВладельцев))
	|			И СтарыеНастройкиПрав.Право = ТаблицаПереименований.СтароеИмя
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПрав.НовоеИмя
	|ИЗ
	|	НастройкиПрав КАК НастройкиПрав
	|
	|СГРУППИРОВАТЬ ПО
	|	НастройкиПрав.Объект,
	|	НастройкиПрав.Пользователь,
	|	НастройкиПрав.НовоеИмя
	|
	|ИМЕЮЩИЕ
	|	НастройкиПрав.НовоеИмя <> """" И
	|	КОЛИЧЕСТВО(НастройкиПрав.НовоеИмя) > 1
	|
	|ОБЪЕДИНИТЬ
	|
	|ВЫБРАТЬ
	|	НастройкиПрав.НовоеИмя
	|ИЗ
	|	НастройкиПрав КАК НастройкиПрав
	|		ЛЕВОЕ СОЕДИНЕНИЕ СтарыеНастройкиПрав КАК СтарыеНастройкиПрав
	|		ПО НастройкиПрав.Объект = СтарыеНастройкиПрав.Объект
	|			И НастройкиПрав.Пользователь = СтарыеНастройкиПрав.Пользователь
	|			И НастройкиПрав.НовоеИмя = СтарыеНастройкиПрав.Право
	|ГДЕ
	|	НЕ СтарыеНастройкиПрав.Право ЕСТЬ NULL 
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПрав.Объект,
	|	НастройкиПрав.Пользователь,
	|	НастройкиПрав.СтароеИмя,
	|	НастройкиПрав.НовоеИмя,
	|	НастройкиПрав.ПравоЗапрещено,
	|	НастройкиПрав.НаследованиеРазрешено,
	|	НастройкиПрав.ПорядокНастройки
	|ИЗ
	|	НастройкиПрав КАК НастройкиПрав";
	// АПК:96-вкл.
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.НастройкиПравОбъектов");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		
		ПовторяющиесяНовыеИмена = РезультатыЗапроса[РезультатыЗапроса.Количество()-2].Выгрузить();
		
		Если ПовторяющиесяНовыеИмена.Количество() > 0 Тогда
			ПовторяющиесяНовыеИменаПрав = "";
			Для каждого Строка Из ПовторяющиесяНовыеИмена Цикл
				ПовторяющиесяНовыеИменаПрав = ПовторяющиесяНовыеИменаПрав
					+ ?(ЗначениеЗаполнено(ПовторяющиесяНовыеИменаПрав), "," + Символы.ПС, "")
					+ Строка.НовоеИмя;
			КонецЦикла;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка в параметрах процедуры %1
				           |общего модуля %2.
				           |
				           |После обновления будут повторяться настройки следующих новых имен прав:
				           |%1.'"),
				"ЗаменитьПраваВНастройкахПравОбъектов",
				"УправлениеДоступом",
				ПовторяющиесяНовыеИменаПрав);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		ТаблицаЗамены = РезультатыЗапроса[РезультатыЗапроса.Количество()-1].Выгрузить();
		
		НаборЗаписей = РегистрыСведений.НастройкиПравОбъектов.СоздатьНаборЗаписей();
		
		ОбновлениеИБ = ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		           Или ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления();
		
		Для каждого Строка Из ТаблицаЗамены Цикл
			НаборЗаписей.Отбор.Объект.Установить(Строка.Объект);
			НаборЗаписей.Отбор.Пользователь.Установить(Строка.Пользователь);
			НаборЗаписей.Отбор.Право.Установить(Строка.СтароеИмя);
			НаборЗаписей.Прочитать();
			Если НаборЗаписей.Количество() > 0 Тогда
				НаборЗаписей.Очистить();
				Если ОбновлениеИБ Тогда
					ОбновлениеИнформационнойБазы.ЗаписатьДанные(НаборЗаписей);
				Иначе
					НаборЗаписей.Записать();
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		
		НоваяЗапись = НаборЗаписей.Добавить();
		Для каждого Строка Из ТаблицаЗамены Цикл
			Если Строка.НовоеИмя = "" Тогда
				Продолжить;
			КонецЕсли;
			НаборЗаписей.Отбор.Объект.Установить(Строка.Объект);
			НаборЗаписей.Отбор.Пользователь.Установить(Строка.Пользователь);
			НаборЗаписей.Отбор.Право.Установить(Строка.НовоеИмя);
			ЗаполнитьЗначенияСвойств(НоваяЗапись, Строка);
			НоваяЗапись.Право = Строка.НовоеИмя;
			Если ОбновлениеИБ Тогда
				ОбновлениеИнформационнойБазы.ЗаписатьДанные(НаборЗаписей);
			Иначе
				НаборЗаписей.Записать();
			КонецЕсли;
		КонецЦикла;
		
		РегистрыСведений.НастройкиПравОбъектов.ОбновитьВспомогательныеДанныеРегистра();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции, используемые для обновления служебных данных.

// Обновляет список ролей пользователей информационной базы по их текущим принадлежностям
// к группам доступа. Пользователи ИБ с ролью "ПолныеПрава" пропускаются.
// 
// Параметры:
//  МассивПользователей - Массив
//                      - Неопределено
//                      - Тип - массив из элементов
//     СправочникСсылка.Пользователи или СправочникСсылка.ВнешниеПользователи.
//     Если Неопределено, то обновить роли всех пользователей.
//     Если Тип = Справочник.ВнешниеПользователи, то будут обновлены роли всех внешних пользователей,
//     иначе будут обновлены роли всех пользователей.
//
//  ПарольПользователяСервиса - Строка - пароль для авторизации в менеджере сервиса.
//
Процедура ОбновитьРолиПользователей(Знач МассивПользователей = Неопределено, Знач ПарольПользователяСервиса = Неопределено) Экспорт
	
	УправлениеДоступомСлужебный.ОбновитьРолиПользователей(МассивПользователей, ПарольПользователяСервиса);
	
КонецПроцедуры

// Обновляет содержание регистров ЗначенияГруппДоступа и ЗначенияГруппДоступаПоУмолчанию,
// которые заполняются на основании настроек в группах доступа и использования видов доступа.
//
Процедура ОбновитьРазрешенныеЗначенияПриИзмененииИспользованияВидовДоступа() Экспорт
	
	РегистрыСведений.ИспользуемыеВидыДоступа.ОбновитьДанныеРегистра();
	
КонецПроцедуры

// Выполняет последовательное заполнение и частичное обновление данных, необходимых для работы
// подсистемы УправлениеДоступом в режиме ограничения доступа на уровне записей.
// 
// При включенном режиме ограничения доступа на уровне записей заполняет наборы
// значений доступа. Заполнение выполняется частями при каждом запуске, пока все
// наборы значений доступа не будут заполнены.
//
// При отключении режима ограничения доступа на уровне записей наборы значений доступа
// (заполненные ранее) удаляются при перезаписи объектов, а не все сразу.
//
// Независимо от режима ограничения доступа на уровне записей обновляет вторичные данные -
// группы значений доступа и дополнительные поля в существующих наборах значений доступа.
// После завершения всех обновлений и заполнений отключает использование регламентного задания.
//
// Сведения о состоянии работы записываются в журнал регистрации.
// Возможно вызывать программно, например при обновлении информационной базы.
//
// Параметры:
//  КоличествоДанных - Число - возвращаемое значение. Количество объектов данных, 
//                             для которых было выполнено заполнение.
//
Процедура ЗаполнениеДанныхДляОграниченияДоступа(КоличествоДанных = 0) Экспорт
	
	УправлениеДоступомСлужебный.ЗаполнениеДанныхДляОграниченияДоступа(КоличествоДанных);
	
КонецПроцедуры

// Для ускорения пакетной обработки данных в текущем сеансе (полноправный пользователь)
// отключает и включает расчет прав при записи объекта или набора записей
// (обновление ключей доступа к объектам и к записям регистров, а также прав
//  групп доступа, пользователей и внешних пользователей на новые ключи доступа).
//
// Рекомендуется:
// - при восстановлении из резервной копии XML;
// - массовой загрузке данных из файла;
// - массовой загрузке данных при обмене данными;
// - групповом изменении объектов.
//
// Параметры:
//  Отключить - Булево - Истина - отключает обновление ключей доступа и включает режим
//                         сбора состава таблиц (списков), для которых будет запланировано
//                         обновление ключей доступа при продолжении обновления ключей доступа.
//                       Ложь - планирует обновление ключей доступа таблиц, собранных в режиме
//                         отключения, и включает стандартный режим обновления ключей доступа.
//
//  ПланироватьОбновление - Булево - планирование обновления при отключении и продолжении.
//                            Когда Отключить = Истина, тогда определяет необходимость сбора
//                              состава таблиц, для которых будет запланировано обновление.
//                              Ложь - требуется только в режиме загрузки из резервной копии XML, когда
//                              загружаются все данные информационной базы, включая все служебные данные.
//                            Когда Отключить = Ложь, тогда определяет необходимость планирования
//                              обновления для собранных таблиц.
//                              Ложь требуется в обработке исключения после отмены транзакции,
//                              если есть внешняя транзакция, так как любая запись
//                              в базу данных в таком состоянии приведет к ошибке, кроме того,
//                              планировать обновление после отмены транзакции не требуется.
// Пример:
//
//  Вариант 1. Запись набора объектов вне транзакции (ТранзакцияАктивна() = Ложь).
//
//	УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Истина);
//	Попытка
//		// Запись набора объектов.
//		// ...
//		УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь);
//	Исключение
//		УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь);
//		//...
//		ВызватьИсключение;
//	КонецПопытки;
//
//  Вариант 2. Запись набора объектов в транзакции (ТранзакцияАктивна() = Истина).
//
//	УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Истина);
//	НачатьТранзакцию();
//	Попытка
//		БлокировкаДанных.Заблокировать();
//		// ...
//		// Запись набора объектов.
//		// ...
//		УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь);
//		ЗафиксироватьТранзакцию();
//	Исключение
//		ОтменитьТранзакцию();
//		УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа(Ложь, Ложь);
//		//...
//		ВызватьИсключение;
//	КонецПопытки;
//
Процедура ОтключитьОбновлениеКлючейДоступа(Отключить, ПланироватьОбновление = Истина) Экспорт
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	Если Отключить И Не Пользователи.ЭтоПолноправныйПользователь() Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный вызов процедуры %1 общего модуля %2.
			           |Отключение обновления ключей доступа возможно только для
			           |полноправного пользователя или в привилегированном режиме.'"),
			"ОтключитьОбновлениеКлючейДоступа",
			"УправлениеДоступом");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ОтключениеОбновления = ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа; // См. УправлениеДоступомСлужебный.НовоеОтключениеОбновленияКлючейДоступа
	Стандартное = Отключить И    ПланироватьОбновление;
	Полное      = Отключить И Не ПланироватьОбновление;
	
	Если ОтключениеОбновления.Стандартное = Стандартное
	   И ОтключениеОбновления.Полное      = Полное Тогда
		Возврат;
	КонецЕсли;
	
	ОтключениеОбновления = Новый Структура(ОтключениеОбновления);
	
	Если Не Отключить И ПланироватьОбновление Тогда
		ИзмененныеСписки = ОтключениеОбновления.ИзмененныеСписки.Получить();
		Если ИзмененныеСписки.Количество() > 0 Тогда
			Если УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
				Списки = Новый Массив;
				ДобавленныеСписки = Новый Соответствие;
				Для Каждого КлючИЗначение Из ИзмененныеСписки Цикл
					ПолноеИмя = Метаданные.НайтиПоТипу(КлючИЗначение.Ключ).ПолноеИмя();
					Списки.Добавить(ПолноеИмя);
					ДобавленныеСписки.Вставить(ПолноеИмя, Истина);
				КонецЦикла;
				НедоступныеСписки = Новый Массив;
				УправлениеДоступомСлужебный.ДобавитьЗависимыеСписки(Списки, ДобавленныеСписки, НедоступныеСписки);
				ПараметрыПланирования = УправлениеДоступомСлужебный.ПараметрыПланированияОбновленияДоступа();
				ПараметрыПланирования.РазрешенныеКлючиДоступа = Ложь;
				ПараметрыПланирования.Описание = "ОтключитьОбновлениеКлючейДоступаПриОкончанииОтключения";
				УправлениеДоступомСлужебный.ЗапланироватьОбновлениеДоступа(Списки, ПараметрыПланирования);
				Если НедоступныеСписки.Количество() > 0 Тогда
					УправлениеДоступомСлужебный.ЗапланироватьОбновлениеДоступа(НедоступныеСписки, ПараметрыПланирования);
				КонецЕсли;
			КонецЕсли;
			ОтключениеОбновления.ИзмененныеСписки = Новый ХранилищеЗначения(Новый Соответствие);
			УправлениеДоступомСлужебныйПовтИсп.КэшИзмененныхСписковПриОтключенномОбновленииКлючейДоступа().Очистить();
		КонецЕсли;
	КонецЕсли;
	
	Если Не Стандартное И Не Полное Тогда
		Если ОтключениеОбновления.ВложенныеОтключения.Количество() > 0 Тогда
			ВложенныеОтключения = Новый Массив(ОтключениеОбновления.ВложенныеОтключения);
			Стандартное = ВложенныеОтключения[0].Стандартное;
			Полное      = ВложенныеОтключения[0].Полное;
			ВложенныеОтключения.Удалить(0);
			ОтключениеОбновления.ВложенныеОтключения = Новый ФиксированныйМассив(ВложенныеОтключения);
		КонецЕсли;
	ИначеЕсли ОтключениеОбновления.Стандартное Или ОтключениеОбновления.Полное Тогда
		Если ОтключениеОбновления.Полное Тогда
			Стандартное = Ложь;
			Полное = Истина;
		КонецЕсли;
		ВложенноеОтключение = Новый Структура;
		ВложенноеОтключение.Вставить("Стандартное", ОтключениеОбновления.Стандартное);
		ВложенноеОтключение.Вставить("Полное",      ОтключениеОбновления.Полное);
		ВложенныеОтключения = Новый Массив(ОтключениеОбновления.ВложенныеОтключения);
		ВложенныеОтключения.Добавить(Новый ФиксированнаяСтруктура(ВложенноеОтключение));
		ОтключениеОбновления.ВложенныеОтключения = Новый ФиксированныйМассив(ВложенныеОтключения);
	КонецЕсли;
	
	ОтключениеОбновления.Стандартное = Стандартное;
	ОтключениеОбновления.Полное      = Полное;
	
	ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа = Новый ФиксированнаяСтруктура(ОтключениеОбновления);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Добавляет отложенный обработчик обновления, который включает универсальное ограничение доступа
// (включает константу ОграничиватьДоступНаУровнеЗаписейУниверсально).
// Следует использовать только в конечных типовых решениях, а не в поставках библиотек.
//
// Для файловой ИБ обработчик не добавляется (кроме начального заполнения).
// Соответственно, в клиент-серверных базах с РИБ (кроме модели сервиса) обработчик тоже не добавляется,
// так как в РИБ могут быть файловые ИБ (если известно обратное, поведение можно переопределить).
//
// Параметры:
//  Версия      - Строка - версия для таблицы ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления.
//                  Указать пустую строку, если параметр ТолькоНачальноеЗаполнение указан Истина.
//  Обработчики - см. ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления
//  ТолькоНачальноеЗаполнение - Булево - добавить только обработчик начального заполнения.
//                  Требуется, если пока не планируется переключать все базы на производительный вариант,
//                  а планируется переключать только новые созданные базы.
//  БезУчетаРИБ - Булево - добавить обработчик обновления без учета наличия РИБ.
//                  Требуется, если планируется переключать клиент-серверные базы на производительный вариант,
//                  которые участвуют в РИБ, так как известно, что в РИБ не участвуют файловые базы или среди
//                  них нет файловых баз со значительным количеством активных пользователей, одновременно
//                  изменяющих данные для которых настроены ограничения на уровне записей.
//
// Пример:
//	Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
//		УправлениеДоступом.ДобавитьОбработчикОбновленияДляВключенияУниверсальногоОграничения("3.0.3.7", Обработчики);
//	КонецПроцедуры
//
Процедура ДобавитьОбработчикОбновленияДляВключенияУниверсальногоОграничения(Версия, Обработчики,
			ТолькоНачальноеЗаполнение = Ложь, БезУчетаРИБ = Ложь) Экспорт
	
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных()
	   И УправлениеДоступомСлужебный.ОграничиватьДоступНаУровнеЗаписейУниверсально(Истина) Тогда
		Возврат;
	КонецЕсли;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "РегистрыСведений.ПараметрыОграниченияДоступа.ВключитьОграничениеДоступаНаУровнеЗаписейУниверсально";
	Обработчик.РежимВыполнения = "Оперативно";
	
	Если ТолькоНачальноеЗаполнение
	 Или ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		Возврат;
	КонецЕсли;
	
	ВключенРИБ = Ложь;
	Если Не БезУчетаРИБ
	   И Не ОбщегоНазначения.РазделениеВключено()
	   И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		
		ИменаПлановОбмена = Новый Массив;
		Для Каждого ПланОбмена Из Метаданные.ПланыОбмена Цикл
			Если ПланОбмена.РаспределеннаяИнформационнаяБаза Тогда
				ИменаПлановОбмена.Добавить(ПланОбмена.Имя);
			КонецЕсли;
		КонецЦикла;
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		Таблица = МодульОбменДаннымиСервер.ТаблицаМонитораОбменаДанными(ИменаПлановОбмена);
		Граница = ТекущаяДатаСеанса() - ('00010701' - '00010101');
		Для Каждого Строка Из Таблица Цикл
			Если Не ЗначениеЗаполнено(Строка.ДатаПоследнегоЗапуска)
			 Или Строка.ДатаПоследнегоЗапуска > Граница Тогда
				ВключенРИБ = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если ВключенРИБ Тогда
		Возврат;
	КонецЕсли;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = Версия;
	Обработчик.Процедура = "РегистрыСведений.ПараметрыОграниченияДоступа.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.ЗапускатьИВПодчиненномУзлеРИБСФильтрами = Истина;
	Обработчик.Комментарий = НСтр("ru = 'Включение универсального ограничения доступа на уровне записей.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("74cb1992-c9ac-4b46-90db-810544dee86c");
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.ПараметрыОграниченияДоступа.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты = "РегистрСведений.ПараметрыОграниченияДоступа";
	Обработчик.ИзменяемыеОбъекты = "РегистрСведений.ПараметрыОграниченияДоступа";
	
КонецПроцедуры

#Область ДляВызоваИзДругихПодсистем

// СлужебныеПодсистемы.ОбъектыУТКАУП

// Назначение: для универсального журнала документов на регистре (ERP).
// Используется для скрытия дублирующей записи в журнале для документов перемещения,
// в тех случаях, когда точно известно, что записей будет сразу две.
//
// Проверяет есть ли ограничение таблицы по указанному виду доступа.
//
// Если для всех видов доступа таблицы хотя бы в одной группе доступа, предоставляющей права к
// указанной таблице, не настроено ограничение (все значения разрешены для всех видов доступа),
// тогда нет ограничения по указанному виду доступа, в противном случае, нет ограничения
// по указанному виду доступа, если только его нет во всех группах доступа для указанной таблицы.
// 
// Параметры:
//  Таблица        - Строка - полное имя объекта метаданных, например, "Документ.РасходнаяНакладная".
//  ВидДоступа     - Строка - имя вида доступа, например, "Организации".
//  ВсеВидыДоступа - Строка - имена всех видов доступа, которые используются в ограничении таблицы,
//                            например, "Организации,ГруппыПартнеров,Склады".
//
// Возвращаемое значение:
//  Булево - если Истина, то есть ограничение таблицы по указанному виду доступа.
// 
Функция ЕстьОграничениеТаблицыПоВидуДоступа(Таблица, ВидДоступа, ВсеВидыДоступа) Экспорт
	
	Возврат УправлениеДоступомСлужебный.ЕстьОграничениеТаблицыПоВидуДоступа(Таблица,
		ВидДоступа, ВсеВидыДоступа);
	
КонецФункции

// Конец СлужебныеПодсистемы.ОбъектыУТКАУП

// Разработка.РазработкаПравИОграниченийДоступа

// Назначение: для вызова из конструктора ограничений СППР.
// 
// Параметры:
//  ОсновнаяТаблица  - Строка - полное имя основной таблицы объекта метаданных, например, "Документ.ЗаказПокупателя".
//  ТекстОграничения - Строка - текст ограничения, который указывается в модуле менеджера
//    объекта метаданных для ограничения пользователей или ограничения внешних пользователей.
//
// Возвращаемое значение:
//  Структура:
//   * ВнутренниеДанные - Структура - данные для передачи в функцию СтруктураОграничения.
//   * ПоляТаблиц       - Соответствие из КлючИЗначение:
//     ** Ключ     - Строка - имя коллекции объектов метаданных, например, Справочники.
//     ** Значение - Соответствие из КлючИЗначение:
//       *** Ключ     - Строка - имя таблицы (объекта метаданных) в верхнем регистре.
//       *** Значение - Структура:
//         **** ТаблицаСуществует - Булево - Ложь (для заполнения Истина, если существует).
//         **** Поля - Соответствие из КлючИЗначение:
//           ***** Ключ - Строка - имя реквизита в верхнем регистре, в том числе через точки,
//                                 например, "ВЛАДЕЛЕЦ.ОРГАНИЗАЦИЯ", "ТОВАРЫ.НОМЕНКЛАТУРА".
//           ***** Значение - Структура:
//             ****** ПолеСОшибкой - Число - 0 (для заполнения, если поле содержит ошибку,
//                       если 1, то ошибка в имени первой части поля,
//                       если 2, то ошибка в имени второй части поля, т.е. после первой точки).
//             ****** ВидОшибки - Строка - "НеНайдено", "ТабличнаяЧастьБезПоля",
//                       "ТабличнаяЧастьПослеТочки".
//             ****** Коллекция - Строка - пустая строка (для заполнения, если первая часть
//                       поля существует, т.е. часть поля до первой точки). Варианты: "Реквизиты",
//                      "ТабличныеЧасти", "СтандартныеРеквизиты", "СтандартныеТабличныеЧасти",
//                      "Измерения", "Ресурсы", "Графы", "ПризнакиУчета", "ПризнакиУчетаСубконто",
//                      "РеквизитыАдресации", "СпециальныеПоля". Специальные поля - это
//                      "Значение" - у таблиц "Константа.*",
//                      "Регистратор" и "Период" - у таблиц "Последовательность.*",
//                      "ОбъектПерерасчета", "ВидРасчета" у таблиц "РегистрРасчета.<Имя>.<ИмяПерерасчета>".
//                      Поля после первой точки могут относится только к коллекциям: "Реквизиты",
//                      "СтандартныеРеквизиты", "ПризнакиУчета", "РеквизитыАдресации". Для этих
//                      частей имени поля не требуется уточнять коллекцию.
//             ****** СодержитТипы - Соответствие из КлючИЗначение:
//               ******* Ключ - Строка - полное имя ссылочной таблицы в верхнем регистре.
//               ******* Значение - Структура:
//                 ******** ИмяТипа     - Строка - имя типа, наличие которого нужно проверить.
//                 ******** СодержитТип - Булево - Ложь (для заполнения Истина,
//                                                         если у поля последнего поля есть тип).
//         **** Предопределенные - Соответствие из КлючИЗначение:
//           ***** Ключ - Строка - имя предопределенного элемента.
//           ***** Значение - Структура:
//             ****** ИмяСуществует - Булево - Ложь (для заполнения Истина, если предопределенный есть).
//
//         **** Расширения - Соответствие из КлючИЗначение:
//           ***** Ключ - Строка - имя третьего имени таблицы, например, имя табличной части.
//           ***** Значение - Структура:
//             ****** ТаблицаСуществует - Булево - Ложь (для заполнения Истина, если существует).
//             ****** Поля - Соответствие - со свойствами, как для основной таблицы (см. выше).
//
Функция РазобранноеОграничение(ОсновнаяТаблица, ТекстОграничения) Экспорт
	
	Возврат УправлениеДоступомСлужебный.РазобранноеОграничение(ОсновнаяТаблица, ТекстОграничения);
	
КонецФункции

// Назначение: для вызова из конструктора ограничений СППР.
// Перед передачей параметра РазобранноеОграничение в свойстве ПоляТаблиц должны быть заполнены
// вложенные свойства ТаблицаСуществует, ПолеСОшибкой, СодержитТип, ИмяСуществует.
// 
// Параметры:
//  РазобранноеОграничение - см. РазобранноеОграничение
//
// Возвращаемое значение:
//  Структура:
//   * ОписаниеОшибок - Структура:
//      ** ЕстьОшибки  - Булево - если Истина, значит найдена одна или более ошибок.
//      ** ТекстОшибок - Строка - текст всех ошибок.
//      ** Ограничение - Строка - пронумерованный текст ограничения со вставками "<<?>>".
//      ** Ошибки      - Массив из Структура - описания отдельных ошибок:
//         *** НомерСтроки    - Число -  строка в многострочном тексте, в которой найдена ошибка.
//         *** ПозицияВСтроке - Число -  номер символа, начиная с которого обнаружена ошибка,
//                                       может быть за пределами строки (длина строки + 1).
//         *** ТекстОшибки    - Строка - текст ошибки без описания позиции.
//         *** СтрокаОшибки   - Строка - строка, в которой найдена ошибка со вставкой "<<?>>".
//      ** Дополнение - Строка - описание вариантов первых ключевых слов частей ограничения.
//
//   * ДополнительныеТаблицы - Массив из Структура:
//      ** Таблица           - Строка - полное имя объекта метаданных.
//      ** Псевдоним         - Строка - имя псевдонима таблицы.
//      ** УсловиеСоединения - Структура - как у свойства ОграничениеИзменения, но только
//                                     узлы: "Поле", "Значение", "Константа", "И", "=".
//   * ПсевдонимОсновнойТаблицы - Строка - заполнено, если указаны дополнительные таблицы.
//   * ОграничениеЧтения    - Структура - как у свойства ОграничениеИзменения.
//   * ОграничениеИзменения - Структура:
//
//      ** Узел - Строка - одна из строк "Поле", "Значение", "Константа",
//           "И", "Или", "Не", "=", "<>", "В", "ЕстьNull", "Тип", "ТипЗначения", "Выбор",
//           "ЗначениеРазрешено",      "ЭтоАвторизованныйПользователь",
//           "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено",
//           "ЧтениеСпискаРазрешено",  "ИзменениеСпискаРазрешено",
//           "ДляВсехСтрок",           "ДляОднойИзСтрок".
//
//     Свойства узла "Поле".
//       ** Имя       - Строка - имя поля, например, "Организация" или "ОсновнаяОрганизация".
//       ** Таблица   - Строка - имя таблицы этого поля (или пустая строка для основной таблицы).
//       ** Псевдоним - Строка - имя псевдонима присоединяемой таблицы этого поля (или пустая строка для основной таблицы),
//                        например, "РегистрСведенийНастройки" для поля "ОсновнаяОрганизация".
//       ** Выразить  - Строка - имя таблицы (если используется), например, для описания поля в виде:
//                       "ВЫРАЗИТЬ(ВЫРАЗИТЬ(Владелец КАК Справочник.Файлы).ВладелецФайла КАК Справочник.Организации).Ссылка".
//       ** Вложение  - Структура - узел Поле, содержащий вложенное действие ВЫРАЗИТЬ (с или без ЕстьNull).
//                    - Неопределено - нет вложенного поля.
//       ** ЕстьNull  - Структура - узел Значение Или Константа, например, для описания выражения вида
//                        "ЕстьNULL(Владелец, Значение(Справочник.Файлы.ПустаяСсылка))".
//                    - Неопределено - если ЕстьNull не используется (в том числе, когда свойство Вложение заполнено).
//
//     Свойства узла "Значение".
//       ** Имя - Строка - имя значения, например, "Справочник.Организации.Основная",
//                                                 "Справочник.Организации.ПустаяСсылка".
//
//     Свойства узла "Константа".
//       ** Значение - Булево
//                   - Число  - произвольное целое число до 16 разрядов
//                   - Строка - произвольная строка до 150 символов
//                   - Неопределено
//
//     Свойства узлов "И", "Или".
//       ** Аргументы - Массив - с элементами:
//            *** Значение - Структура - любой узел, кроме Значение и Константа.
//
//     Свойства узла "Не".
//       ** Аргумент - Структура - любой узел, кроме Значение и Константа.
//
//     Свойства узлов "=", "<>".
//       ** ПервыйАргумент - Структура - узел Поле.
//       ** ВторойАргумент - Структура - узел Значение, Константа. А узел Поле только для условия соединения.
//
//     Свойства узла "В".
//       ** Искомое  - Структура - узел Поле.
//       ** Значения - Массив - с элементами:
//            *** Значение - Структура - узлы Значение и/или Константа.
//
//     Свойства узла "ЕстьNull".
//       ** Аргумент - Структура - узел Поле (выражение вида "<Поле> ЕСТЬ NULL").
//
//     Свойства узла "Тип".
//       ** Имя - Строка - имя таблицы, например, "Справочник.Организации".
//
//     Свойства узла "ТипЗначения".
//       ** Аргумент - Структура - узел Поле.
//
//     Свойства узла "Выбор".
//       ** Выбор - Структура - узел Поле.
//                - Неопределено - условия содержат выражение, а не узел Значение.
//       ** Когда - Массив - с элементами:
//            *** Значение - Структура:
//                  **** Условие  - Структура - узел Значение, если свойство Выбор указано, в противном случае
//                                              узлы И, Или, Не, =, <>, В (распространяется на вложенное содержимое).
//                  **** Значение - Структура - узел, кроме Выбор.
//       ** Иначе - Структура - узел, кроме ВЫБОР и Значение (Поле и Константа может быть только типа Булево).
//
//     Свойства узлов "ЗначениеРазрешено",      "ЭтоАвторизованныйПользователь",
//                    "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено",
//                    "ЧтениеСпискаРазрешено",  "ИзменениеСпискаРазрешено".
//       ** Поле - Структура - узел Поле.
//       ** Типы - Массив - с элементами:
//            *** Значение - Строка - полное имя таблицы
//       ** ПроверятьТипыКромеУказанных - Булево - если Истина, то все типы свойства Поле,
//                                                 кроме указанных в свойстве Типы.
//       ** УточненияСравнения - Соответствие из КлючИЗначение:
//            *** Ключ     - Строка - уточняемое значение "Неопределено", "Null", "ПустаяСсылка",
//                                    <полное имя таблицы>, "Число", "Строка", "Дата", "Булево".
//            *** Значение - Строка - результат "Ложь", "Истина".
//
//     Свойства узлов "ДляВсехСтрок", "ДляОднойИзСтрок".
//       ** Аргумент - Структура - любой узел.
//
Функция СтруктураОграничения(РазобранноеОграничение) Экспорт
	
	Возврат УправлениеДоступомСлужебный.СтруктураОграничения(РазобранноеОграничение);
	
КонецФункции

// Конец Разработка.РазработкаПравИОграниченийДоступа

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Дополнение к процедуре ЗаполнитьНаборыЗначенийДоступа.

// Приводит таблицу наборов значений к формату табличной части или набора записей.
//  Выполняется перед записью в регистр НаборыЗначенийДоступа или
// перед записью объекта с табличной частью НаборыЗначенийДоступа.
//
// Параметры:
//  СсылкаНаОбъект - ЛюбаяСсылка - ссылка на объект из ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект,
//                                 для которого заполняются наборы значений доступа.
//
//  Таблица - см. ТаблицаНаборыЗначенийДоступа
//
Процедура УточнитьНаборыЗначенийДоступа(СсылкаНаОбъект, Таблица)
	
	ИменаВидовДоступа = УправлениеДоступомСлужебный.СвойстваВидовДоступа().ПоИменам;
	
	ВозможныеПрава = УправлениеДоступомСлужебный.ВозможныеПраваДляНастройкиПравОбъектов();
	ТипыВладельцевНастроекПрав = ВозможныеПрава.ПоТипамСсылок;
	
	Для каждого Строка Из Таблица Цикл
		
		Если ТипыВладельцевНастроекПрав.Получить(ТипЗнч(Строка.ЗначениеДоступа)) <> Неопределено
		   И НЕ ЗначениеЗаполнено(Строка.Уточнение) Тогда
			
			Строка.Уточнение = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ТипЗнч(СсылкаНаОбъект));
		КонецЕсли;
		
		Если Строка.ВидДоступа = "" Тогда
			Продолжить;
		КонецЕсли;
		
		Если Строка.ВидДоступа = "ПравоЧтения"
		 ИЛИ Строка.ВидДоступа = "ПравоИзменения" Тогда
			
			Если ТипЗнч(Строка.ЗначениеДоступа) <> Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных") Тогда
				Строка.ЗначениеДоступа =
					ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ТипЗнч(Строка.ЗначениеДоступа));
			КонецЕсли;
			
			Если Строка.ВидДоступа = "ПравоЧтения" Тогда
				Строка.Уточнение = Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка();
			Иначе
				Строка.Уточнение = Строка.ЗначениеДоступа;
			КонецЕсли;
		
		ИначеЕсли ИменаВидовДоступа.Получить(Строка.ВидДоступа) <> Неопределено
		      ИЛИ Строка.ВидДоступа = "НастройкиПрав" Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Объект ""%1"" сформировал набор значений доступа,
				           |содержащий известный вид доступа ""%2"", который не требуется указывать.
				           |
				           |Указывать требуется только специальные виды доступа
				           |""%3"", ""%4"", если они используются.'"),
				ТипЗнч(СсылкаНаОбъект),
				Строка.ВидДоступа,
				"ПравоЧтения",
				"ПравоИзменения");
			ВызватьИсключение ТекстОшибки;
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Объект ""%1"" сформировал набор значений доступа,
				           |содержащий неизвестный вид доступа ""%2"".'"),
				ТипЗнч(СсылкаНаОбъект),
				Строка.ВидДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Строка.ВидДоступа = "";
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьНаборыЗначенийДоступа.

Функция НаборыТаблицы(Таблица, НормализацияПрав = Ложь)
	
	НаборыТаблицы = Новый Соответствие;
	
	Для каждого Строка Из Таблица Цикл
		Набор = НаборыТаблицы.Получить(Строка.НомерНабора);
		Если Набор = Неопределено Тогда
			Набор = Новый Структура;
			Набор.Вставить("Чтение", Ложь);
			Набор.Вставить("Изменение", Ложь);
			Набор.Вставить("Строки", Новый Массив);
			НаборыТаблицы.Вставить(Строка.НомерНабора, Набор);
		КонецЕсли;
		Если Строка.Чтение Тогда
			Набор.Чтение = Истина;
		КонецЕсли;
		Если Строка.Изменение Тогда
			Набор.Изменение = Истина;
		КонецЕсли;
		Набор.Строки.Добавить(Строка);
	КонецЦикла;
	
	Если НормализацияПрав Тогда
		Для каждого ОписаниеНабора Из НаборыТаблицы Цикл
			Набор = ОписаниеНабора.Значение;
			
			Если НЕ Набор.Чтение И НЕ Набор.Изменение Тогда
				Набор.Чтение    = Истина;
				Набор.Изменение = Истина;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат НаборыТаблицы;
	
КонецФункции

Процедура ДобавитьНаборы(Приемник, Источник)
	
	НаборыПриемника = НаборыТаблицы(Приемник);
	НаборыИсточника = НаборыТаблицы(Источник);
	
	МаксимальныйНомерНабора = -1;
	
	Для каждого ОписаниеНабораПриемника Из НаборыПриемника Цикл
		НаборПриемника = ОписаниеНабораПриемника.Значение;
		
		Если НЕ НаборПриемника.Чтение И НЕ НаборПриемника.Изменение Тогда
			НаборПриемника.Чтение    = Истина;
			НаборПриемника.Изменение = Истина;
		КонецЕсли;
		
		Для каждого Строка Из НаборПриемника.Строки Цикл
			Строка.Чтение    = НаборПриемника.Чтение;
			Строка.Изменение = НаборПриемника.Изменение;
		КонецЦикла;
		
		Если ОписаниеНабораПриемника.Ключ > МаксимальныйНомерНабора Тогда
			МаксимальныйНомерНабора = ОписаниеНабораПриемника.Ключ;
		КонецЕсли;
	КонецЦикла;
	
	НомерНовогоНабора = МаксимальныйНомерНабора + 1;
	
	Для каждого ОписаниеНабораИсточника Из НаборыИсточника Цикл
		НаборИсточника = ОписаниеНабораИсточника.Значение;
		
		Если НЕ НаборИсточника.Чтение И НЕ НаборИсточника.Изменение Тогда
			НаборИсточника.Чтение    = Истина;
			НаборИсточника.Изменение = Истина;
		КонецЕсли;
		
		Для каждого СтрокаИсточника Из НаборИсточника.Строки Цикл
			НоваяСтрока = Приемник.Добавить();
			ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
			НоваяСтрока.НомерНабора = НомерНовогоНабора;
			НоваяСтрока.Чтение      = НаборИсточника.Чтение;
			НоваяСтрока.Изменение   = НаборИсточника.Изменение;
		КонецЦикла;
		
		НомерНовогоНабора = НомерНовогоНабора + 1;
	КонецЦикла;
	
КонецПроцедуры

Процедура УмножитьНаборы(Приемник, Источник)
	
	НаборыПриемника = НаборыТаблицы(Приемник);
	НаборыИсточника = НаборыТаблицы(Источник, Истина);
	Таблица = ТаблицаНаборыЗначенийДоступа();
	
	ТекущийНомерНабора = 1;
	Для каждого ОписаниеНабораПриемника Из НаборыПриемника Цикл
			НаборПриемника = ОписаниеНабораПриемника.Значение;
		
		Если НЕ НаборПриемника.Чтение И НЕ НаборПриемника.Изменение Тогда
			НаборПриемника.Чтение    = Истина;
			НаборПриемника.Изменение = Истина;
		КонецЕсли;
		
		Для каждого ОписаниеНабораИсточника Из НаборыИсточника Цикл
			НаборИсточника = ОписаниеНабораИсточника.Значение;
			
			УмножениеЧтения    = НаборПриемника.Чтение    И НаборИсточника.Чтение;
			УмножениеИзменения = НаборПриемника.Изменение И НаборИсточника.Изменение;
			Если НЕ УмножениеЧтения И НЕ УмножениеИзменения Тогда
				Продолжить;
			КонецЕсли;
			Для каждого СтрокаПриемника Из НаборПриемника.Строки Цикл
				Строка = Таблица.Добавить();
				ЗаполнитьЗначенияСвойств(Строка, СтрокаПриемника);
				Строка.НомерНабора = ТекущийНомерНабора;
				Строка.Чтение      = УмножениеЧтения;
				Строка.Изменение   = УмножениеИзменения;
			КонецЦикла;
			Для каждого СтрокаИсточника Из НаборИсточника.Строки Цикл
				Строка = Таблица.Добавить();
				ЗаполнитьЗначенияСвойств(Строка, СтрокаИсточника);
				Строка.НомерНабора = ТекущийНомерНабора;
				Строка.Чтение      = УмножениеЧтения;
				Строка.Изменение   = УмножениеИзменения;
			КонецЦикла;
			ТекущийНомерНабора = ТекущийНомерНабора + 1;
		КонецЦикла;
	КонецЦикла;
	
	Приемник = Таблица;
	
КонецПроцедуры

Процедура ДобавитьНаборыИУпростить(Приемник, Источник)
	
	НаборыПриемника = НаборыТаблицы(Приемник);
	НаборыИсточника = НаборыТаблицы(Источник);
	
	НаборыРезультата   = Новый Соответствие;
	КодыТипов          = Новый Соответствие;
	КодыПеречислений   = Новый Соответствие;
	ТаблицаСтрокНабора = Новый ТаблицаЗначений;
	
	ЗаполнитьКодыТиповИТаблицуСтрокНабора(КодыТипов, КодыПеречислений, ТаблицаСтрокНабора);
	
	ТекущийНомерНабора = 1;
	
	ДобавитьНаборыКРезультатуСУпрощением(
		НаборыРезультата, НаборыПриемника, ТекущийНомерНабора, КодыТипов, КодыПеречислений, ТаблицаСтрокНабора);
	
	ДобавитьНаборыКРезультатуСУпрощением(
		НаборыРезультата, НаборыИсточника, ТекущийНомерНабора, КодыТипов, КодыПеречислений, ТаблицаСтрокНабора);
	
	ЗаполнитьПриемникПоНаборамРезультата(Приемник, НаборыРезультата);
	
КонецПроцедуры

Процедура УмножитьНаборыИУпростить(Приемник, Источник)
	
	НаборыПриемника = НаборыТаблицы(Приемник);
	НаборыИсточника = НаборыТаблицы(Источник, Истина);
	
	НаборыРезультата   = Новый Соответствие;
	КодыТипов          = Новый Соответствие;
	КодыПеречислений   = Новый Соответствие;
	ТаблицаСтрокНабора = Новый ТаблицаЗначений;
	
	ЗаполнитьКодыТиповИТаблицуСтрокНабора(КодыТипов, КодыПеречислений, ТаблицаСтрокНабора);
	
	ТекущийНомерНабора = 1;
	
	Для каждого ОписаниеНабораПриемника Из НаборыПриемника Цикл
		НаборПриемника = ОписаниеНабораПриемника.Значение;
		
		Если НЕ НаборПриемника.Чтение И НЕ НаборПриемника.Изменение Тогда
			НаборПриемника.Чтение    = Истина;
			НаборПриемника.Изменение = Истина;
		КонецЕсли;
		
		Для каждого ОписаниеНабораИсточника Из НаборыИсточника Цикл
			НаборИсточника = ОписаниеНабораИсточника.Значение;
			
			УмножениеЧтения    = НаборПриемника.Чтение    И НаборИсточника.Чтение;
			УмножениеИзменения = НаборПриемника.Изменение И НаборИсточника.Изменение;
			Если НЕ УмножениеЧтения И НЕ УмножениеИзменения Тогда
				Продолжить;
			КонецЕсли;
			
			СтрокиНабора = ТаблицаСтрокНабора.Скопировать();
			
			Для каждого СтрокаПриемника Из НаборПриемника.Строки Цикл
				Строка = СтрокиНабора.Добавить();
				Строка.ВидДоступа      = СтрокаПриемника.ВидДоступа;
				Строка.ЗначениеДоступа = СтрокаПриемника.ЗначениеДоступа;
				Строка.Уточнение       = СтрокаПриемника.Уточнение;
				ЗаполнитьИдентификаторСтроки(Строка, КодыТипов, КодыПеречислений);
			КонецЦикла;
			Для каждого СтрокаИсточника Из НаборИсточника.Строки Цикл
				Строка = СтрокиНабора.Добавить();
				Строка.ВидДоступа      = СтрокаИсточника.ВидДоступа;
				Строка.ЗначениеДоступа = СтрокаИсточника.ЗначениеДоступа;
				Строка.Уточнение       = СтрокаИсточника.Уточнение;
				ЗаполнитьИдентификаторСтроки(Строка, КодыТипов, КодыПеречислений);
			КонецЦикла;
			
			СтрокиНабора.Свернуть("ИдентификаторСтроки, ВидДоступа, ЗначениеДоступа, Уточнение");
			СтрокиНабора.Сортировать("ИдентификаторСтроки");
			
			ИдентификаторНабора = "";
			Для каждого Строка Из СтрокиНабора Цикл
				ИдентификаторНабора = ИдентификаторНабора + Строка.ИдентификаторСтроки + Символы.ПС;
			КонецЦикла;
			
			СуществующийНабор = НаборыРезультата.Получить(ИдентификаторНабора);
			Если СуществующийНабор = Неопределено Тогда
				
				СвойстваНабора = Новый Структура;
				СвойстваНабора.Вставить("Чтение",      УмножениеЧтения);
				СвойстваНабора.Вставить("Изменение",   УмножениеИзменения);
				СвойстваНабора.Вставить("Строки",      СтрокиНабора);
				СвойстваНабора.Вставить("НомерНабора", ТекущийНомерНабора);
				НаборыРезультата.Вставить(ИдентификаторНабора, СвойстваНабора);
				ТекущийНомерНабора = ТекущийНомерНабора + 1;
			Иначе
				Если УмножениеЧтения Тогда
					СуществующийНабор.Чтение = Истина;
				КонецЕсли;
				Если УмножениеИзменения Тогда
					СуществующийНабор.Изменение = Истина;
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	ЗаполнитьПриемникПоНаборамРезультата(Приемник, НаборыРезультата);
	
КонецПроцедуры

Процедура ЗаполнитьКодыТиповИТаблицуСтрокНабора(КодыТипов, КодыПеречислений, ТаблицаСтрокНабора)
	
	КодыПеречислений = УправлениеДоступомСлужебныйПовтИсп.КодыПеречислений();
	
	КодыТипов = УправлениеДоступомСлужебныйПовтИсп.КодыТиповСсылок("ОпределяемыйТип.ЗначениеДоступа");
	
	ДлинаКодаТипа = 0;
	Для каждого КлючИЗначение Из КодыТипов Цикл
		ДлинаКодаТипа = СтрДлина(КлючИЗначение.Значение);
		Прервать;
	КонецЦикла;
	
	ДлинаИдентификатораСтроки =
		20 // Строка имени вида доступа
		+ ДлинаКодаТипа
		+ 36 // Длина строкового представления уникального идентификатора (значения доступа).
		+ 36 // Длина строкового представления уникального идентификатора (уточнения).
		+ 6; // Место для разделителей
	
	ТаблицаСтрокНабора = Новый ТаблицаЗначений;
	ТаблицаСтрокНабора.Колонки.Добавить("ИдентификаторСтроки", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(ДлинаИдентификатораСтроки)));
	ТаблицаСтрокНабора.Колонки.Добавить("ВидДоступа",          Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
	ТаблицаСтрокНабора.Колонки.Добавить("ЗначениеДоступа",     Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	ТаблицаСтрокНабора.Колонки.Добавить("Уточнение",           Новый ОписаниеТипов("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	
КонецПроцедуры

Процедура ЗаполнитьИдентификаторСтроки(Строка, КодыТипов, КодыПеречислений)
	
	Если Строка.ЗначениеДоступа = Неопределено Тогда
		ИдентификаторЗначенияДоступа = "";
	Иначе
		ИдентификаторЗначенияДоступа = КодыПеречислений.Получить(Строка.ЗначениеДоступа);
		Если ИдентификаторЗначенияДоступа = Неопределено Тогда
			ИдентификаторЗначенияДоступа = Строка(Строка.ЗначениеДоступа.УникальныйИдентификатор());
		КонецЕсли;
	КонецЕсли;
	
	Строка.ИдентификаторСтроки = Строка.ВидДоступа + ";"
		+ КодыТипов.Получить(ТипЗнч(Строка.ЗначениеДоступа)) + ";"
		+ ИдентификаторЗначенияДоступа + ";"
		+ Строка.Уточнение.УникальныйИдентификатор() + ";";
	
КонецПроцедуры

Процедура ДобавитьНаборыКРезультатуСУпрощением(НаборыРезультата, ДобавляемыеНаборы, ТекущийНомерНабора, КодыТипов, КодыПеречислений, ТаблицаСтрокНабора)
	
	Для каждого ОписаниеДобавляемогоНабора Из ДобавляемыеНаборы Цикл
		ДобавляемыйНабор = ОписаниеДобавляемогоНабора.Значение;
		
		Если НЕ ДобавляемыйНабор.Чтение И НЕ ДобавляемыйНабор.Изменение Тогда
			ДобавляемыйНабор.Чтение    = Истина;
			ДобавляемыйНабор.Изменение = Истина;
		КонецЕсли;
		
		СтрокиНабора = ТаблицаСтрокНабора.Скопировать();
		
		Для каждого СтрокаДобавляемогоНабора Из ДобавляемыйНабор.Строки Цикл
			Строка = СтрокиНабора.Добавить();
			Строка.ВидДоступа      = СтрокаДобавляемогоНабора.ВидДоступа;
			Строка.ЗначениеДоступа = СтрокаДобавляемогоНабора.ЗначениеДоступа;
			Строка.Уточнение       = СтрокаДобавляемогоНабора.Уточнение;
			ЗаполнитьИдентификаторСтроки(Строка, КодыТипов, КодыПеречислений);
		КонецЦикла;
		
		СтрокиНабора.Свернуть("ИдентификаторСтроки, ВидДоступа, ЗначениеДоступа, Уточнение");
		СтрокиНабора.Сортировать("ИдентификаторСтроки");
		
		ИдентификаторНабора = "";
		Для каждого Строка Из СтрокиНабора Цикл
			ИдентификаторНабора = ИдентификаторНабора + Строка.ИдентификаторСтроки + Символы.ПС;
		КонецЦикла;
		
		СуществующийНабор = НаборыРезультата.Получить(ИдентификаторНабора);
		Если СуществующийНабор = Неопределено Тогда
			
			СвойстваНабора = Новый Структура;
			СвойстваНабора.Вставить("Чтение",      ДобавляемыйНабор.Чтение);
			СвойстваНабора.Вставить("Изменение",   ДобавляемыйНабор.Изменение);
			СвойстваНабора.Вставить("Строки",      СтрокиНабора);
			СвойстваНабора.Вставить("НомерНабора", ТекущийНомерНабора);
			НаборыРезультата.Вставить(ИдентификаторНабора, СвойстваНабора);
			
			ТекущийНомерНабора = ТекущийНомерНабора + 1;
		Иначе
			Если ДобавляемыйНабор.Чтение Тогда
				СуществующийНабор.Чтение = Истина;
			КонецЕсли;
			Если ДобавляемыйНабор.Изменение Тогда
				СуществующийНабор.Изменение = Истина;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Процедура ЗаполнитьПриемникПоНаборамРезультата(Приемник, НаборыРезультата)
	
	Приемник = ТаблицаНаборыЗначенийДоступа();
	
	СписокНаборов = Новый СписокЗначений;
	Для Каждого ОписаниеНабора Из НаборыРезультата Цикл
		СписокНаборов.Добавить(ОписаниеНабора.Значение, ОписаниеНабора.Ключ);
	КонецЦикла;
	СписокНаборов.СортироватьПоПредставлению();
	
	ТекущийНомерНабора = 1;
	Для Каждого ЭлементСписка Из СписокНаборов Цикл
		СвойстваНабора = ЭлементСписка.Значение;
		Для Каждого Строка Из СвойстваНабора.Строки Цикл
			НоваяСтрока = Приемник.Добавить();
			НоваяСтрока.НомерНабора     = ТекущийНомерНабора;
			НоваяСтрока.ВидДоступа      = Строка.ВидДоступа;
			НоваяСтрока.ЗначениеДоступа = Строка.ЗначениеДоступа;
			НоваяСтрока.Уточнение       = Строка.Уточнение;
			НоваяСтрока.Чтение          = СвойстваНабора.Чтение;
			НоваяСтрока.Изменение       = СвойстваНабора.Изменение;
		КонецЦикла;
		ТекущийНомерНабора = ТекущийНомерНабора + 1;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ПриСозданииФормыЗначенияДоступа, ГруппыЗначенийДоступаРазрешающиеИзменениеЗначенийДоступа.
Функция СвойстваГруппЗначенияДоступа(ТипЗначенияДоступа, ЗаголовокОшибки)
	
	УстановитьПривилегированныйРежим(Истина);
	
	СвойстваГрупп = Новый Структура;
	
	СвойстваВидовДоступа = УправлениеДоступомСлужебный.СвойстваВидовДоступа();
	СвойстваВидаДоступа = СвойстваВидовДоступа.ЗначенияДоступаСГруппами.ПоТипам.Получить(ТипЗначенияДоступа); // См. УправлениеДоступомСлужебный.СвойстваВидаДоступа
	
	Если СвойстваВидаДоступа = Неопределено Тогда
		ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для значений доступа типа ""%1""
			           |не используются группы значений доступа.'"),
			Строка(ТипЗначенияДоступа));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	СвойстваГрупп.Вставить("ВидДоступа", СвойстваВидаДоступа.Имя);
	СвойстваГрупп.Вставить("Тип",        СвойстваВидаДоступа.ТипГруппЗначений);
	
	СвойстваГрупп.Вставить("Таблица",    Метаданные.НайтиПоТипу(
		СвойстваВидаДоступа.ТипГруппЗначений).ПолноеИмя());
	
	СвойстваГрупп.Вставить("ПустаяСсылкаТипаЗначений",
		УправлениеДоступомСлужебный.ПустаяСсылкаОбъектаМетаданных(ТипЗначенияДоступа));
	
	Возврат СвойстваГрупп;
	
КонецФункции

// Для функции НастроитьОтборыДинамическогоСписка.
Процедура ОбновитьЗначениеПараметраКомпоновкиДанных(Знач ВладелецПараметров,
                                                    Знач ИмяПараметра,
                                                    Знач ЗначениеПараметра)
	
	Для каждого Параметр Из ВладелецПараметров.Параметры.Элементы Цикл
		Если Строка(Параметр.Параметр) = ИмяПараметра Тогда
			
			Если Параметр.Использование
			   И Параметр.Значение = ЗначениеПараметра Тогда
				Возврат;
			КонецЕсли;
			Прервать;
			
		КонецЕсли;
	КонецЦикла;
	
	ВладелецПараметров.Параметры.УстановитьЗначениеПараметра(ИмяПараметра, ЗначениеПараметра);
	
КонецПроцедуры

// Для процедур ВключитьПрофильПользователю и ВыключитьПрофильПользователю.
Процедура ВключитьОтключитьПрофильПользователя(Пользователь, Профиль, Включить, Источник = Неопределено) Экспорт
	
	Если Не УправлениеДоступомСлужебный.УпрощенныйИнтерфейсНастройкиПравДоступа() Тогда
		ТекстОшибки =
			НСтр("ru = 'Данная операция возможна только для упрощенного
			           |интерфейса настройки прав доступа.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Включить Тогда
		ИмяПроцедурыИлиФункции = "ВключитьПрофильПользователю";
	Иначе
		ИмяПроцедурыИлиФункции = "ВыключитьПрофильПользователю";
	КонецЕсли;
	
	// Проверка типов значения параметра Пользователь.
	Если ТипЗнч(Пользователь) <> Тип("СправочникСсылка.Пользователи")
	   И ТипЗнч(Пользователь) <> Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		
		ИмяПараметра = "Пользователь";
		ЗначениеПараметра = Пользователь;
		Типы = Новый Массив;
		Типы.Добавить(Тип("СправочникСсылка.Пользователи"));
		Типы.Добавить(Тип("СправочникСсылка.ВнешниеПользователи"));
		ОжидаемыеТипы = Новый ОписаниеТипов(Типы);
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимое значение параметра %1 в %2.
			           |Ожидалось: %3; передано значение: %4 (тип %5).'"),
			ИмяПараметра,
			ИмяПроцедурыИлиФункции,
			ОжидаемыеТипы, 
			?(ЗначениеПараметра <> Неопределено, ЗначениеПараметра, НСтр("ru = 'Неопределено'")),
			ТипЗнч(ЗначениеПараметра));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	// Проверка типов значения параметра Профиль.
	Если ТипЗнч(Профиль) <> Тип("СправочникСсылка.ПрофилиГруппДоступа")
	   И ТипЗнч(Профиль) <> Тип("Строка")
	   И ТипЗнч(Профиль) <> Тип("УникальныйИдентификатор")
	   И Не (Не Включить И ТипЗнч(Профиль) = Тип("Неопределено")) Тогда
		
		ИмяПараметра = "Профиль";
		ЗначениеПараметра = Профиль;
		Типы = Новый Массив;
		Типы.Добавить(Тип("СправочникСсылка.ПрофилиГруппДоступа"));
		Типы.Добавить(Тип("Строка"));
		Типы.Добавить(Тип("УникальныйИдентификатор"));
		Если Не Включить Тогда
			Типы.Добавить(Тип("Неопределено"));
		КонецЕсли;
		ОжидаемыеТипы = Новый ОписаниеТипов(Типы);
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимое значение параметра %1 в %2.
			           |Ожидалось: %3; передано значение: %4 (тип %5).'"),
			ИмяПараметра,
			ИмяПроцедурыИлиФункции,
			ОжидаемыеТипы, 
			?(ЗначениеПараметра <> Неопределено, ЗначениеПараметра, НСтр("ru = 'Неопределено'")),
			ТипЗнч(ЗначениеПараметра));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ТипЗнч(Профиль) = Тип("СправочникСсылка.ПрофилиГруппДоступа")
	 Или ТипЗнч(Профиль) = Тип("Неопределено") Тогда
		
		ТекущийПрофиль = Профиль;
	Иначе
		ТекущийПрофиль = Справочники.ПрофилиГруппДоступа.ПоставляемыйПрофильПоИдентификатору(
			Профиль, Истина, Истина);
	КонецЕсли;
	
	Если ТекущийПрофиль <> Неопределено Тогда
		СвойстваПрофиля = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ТекущийПрофиль,
			"Наименование, ВидыДоступа");
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	&УсловиеОтбора";
	Запрос.УстановитьПараметр("Пользователь", Пользователь);
	Если ТекущийПрофиль = ПрофильАдминистратор() Тогда
		УсловиеОтбора = "ГруппыДоступа.Ссылка = &ГруппаДоступаАдминистраторы";
		Запрос.УстановитьПараметр("ГруппаДоступаАдминистраторы",
			ГруппаДоступаАдминистраторы());
	Иначе
		УсловиеОтбора = "ГруппыДоступа.Пользователь = &Пользователь";
		Если Включить Или ТекущийПрофиль <> Неопределено Тогда
			УсловиеОтбора = УсловиеОтбора + Символы.ПС + "	И ГруппыДоступа.Профиль = &Профиль"; // @query-part-1
			Запрос.УстановитьПараметр("Профиль", ТекущийПрофиль);
		КонецЕсли;
	КонецЕсли;
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбора", УсловиеОтбора);
	
	РезультатЗапроса = Запрос.Выполнить();
	Выборка = РезультатЗапроса.Выбрать();
	
	Блокировка = Новый БлокировкаДанных();
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
	ЭлементБлокировки.ИсточникДанных = РезультатЗапроса;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Выборка.Следующий();
		Пока Истина Цикл
			ПерсональнаяГруппаДоступа = Выборка.Ссылка;
			Если ЗначениеЗаполнено(ПерсональнаяГруппаДоступа) Тогда
				ГруппаДоступаОбъект = ПерсональнаяГруппаДоступа.ПолучитьОбъект();
				ГруппаДоступаОбъект.ПометкаУдаления = Ложь;
				
			ИначеЕсли ТекущийПрофиль <> Неопределено Тогда
				// Создание персональной группы доступа.
				ГруппаДоступаОбъект = Справочники.ГруппыДоступа.СоздатьЭлемент();
				ГруппаДоступаОбъект.Родитель     = Справочники.ГруппыДоступа.РодительПерсональныхГруппДоступа();
				ГруппаДоступаОбъект.Наименование = СвойстваПрофиля.Наименование;
				ГруппаДоступаОбъект.Пользователь = Пользователь;
				ГруппаДоступаОбъект.Профиль      = ТекущийПрофиль;
				ЗаполнитьВидыИЗначенияДоступаНовойГруппыДоступа(ГруппаДоступаОбъект,
					СвойстваПрофиля, Источник);
			Иначе
				ГруппаДоступаОбъект = Неопределено;
			КонецЕсли;
			
			Если ПерсональнаяГруппаДоступа = ГруппаДоступаАдминистраторы() Тогда
				ОписаниеПользователя =  ГруппаДоступаОбъект.Пользователи.Найти(
					Пользователь, "Пользователь");
				
				Если Включить И ОписаниеПользователя = Неопределено Тогда
					ГруппаДоступаОбъект.Пользователи.Добавить().Пользователь = Пользователь;
				ИначеЕсли Не Включить И ОписаниеПользователя <> Неопределено Тогда
					ГруппаДоступаОбъект.Пользователи.Удалить(ОписаниеПользователя);
				КонецЕсли;
				
				Если Не ОбщегоНазначения.РазделениеВключено() Тогда
					// Проверка пустого списка пользователей ИБ в группе доступа Администраторы.
					ОписаниеОшибки = "";
					УправлениеДоступомСлужебный.ПроверитьНаличиеПользователяИБВГруппеДоступаАдминистраторы(
						ГруппаДоступаОбъект.Пользователи, ОписаниеОшибки);
					
					Если ЗначениеЗаполнено(ОписаниеОшибки) Тогда
						ТекстОшибки =
							НСтр("ru = 'Профиль Администратор должен быть хотя бы у одного пользователя,
							           |которому разрешен вход в программу.'");
						ВызватьИсключение ТекстОшибки;
					КонецЕсли;
				КонецЕсли;
			ИначеЕсли ГруппаДоступаОбъект <> Неопределено Тогда
				ГруппаДоступаОбъект.Пользователи.Очистить();
				Если Включить Тогда
					ГруппаДоступаОбъект.Пользователи.Добавить().Пользователь = Пользователь;
				КонецЕсли;
			КонецЕсли;
			
			Если ГруппаДоступаОбъект <> Неопределено Тогда
				ГруппаДоступаОбъект.Записать();
			КонецЕсли;
			
			Если Не Выборка.Следующий() Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

Процедура ЗаполнитьВидыИЗначенияДоступаНовойГруппыДоступа(ГруппаДоступаОбъект, СвойстваПрофиля, Источник)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	ГруппыДоступа.Профиль = &Профиль
	|	И ГруппыДоступа.Пользователь = &Пользователь";
	Запрос.УстановитьПараметр("Профиль", ГруппаДоступаОбъект.Профиль);
	Запрос.УстановитьПараметр("Пользователь", Источник);
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() И Выборка.Количество() = 1 Тогда
		СвойстваГруппы = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Выборка.Ссылка,
			"ВидыДоступа, ЗначенияДоступа");
		ГруппаДоступаОбъект.ВидыДоступа.Загрузить(СвойстваГруппы.ВидыДоступа.Выгрузить());
		ГруппаДоступаОбъект.ЗначенияДоступа.Загрузить(СвойстваГруппы.ЗначенияДоступа.Выгрузить());
	Иначе
		ГруппаДоступаОбъект.ВидыДоступа.Загрузить(ВидыДоступаДляНовойГруппыДоступа(СвойстваПрофиля));
	КонецЕсли;
	
КонецПроцедуры

Функция ВидыДоступаДляНовойГруппыДоступа(СвойстваПрофиля)
	
	ВидыДоступа = СвойстваПрофиля.ВидыДоступа.Выгрузить();
	
	Отбор = Новый Структура;
	Отбор.Вставить("Предустановленный", Истина);
	Предустановленные = ВидыДоступа.НайтиСтроки(Отбор);
	
	Для Каждого Предустановленный Из Предустановленные Цикл
		ВидыДоступа.Удалить(Предустановленный);
	КонецЦикла;
	
	Возврат ВидыДоступа;
	
КонецФункции

#КонецОбласти
